Key-Value Observing

原文連接設計模式

Key-Value Observing 鍵值觀察 ,是一種設計模式觀察者模式的實現安全

官方定義編碼

鍵值觀察提供了一種機制,容許對象通知其餘對象的特定屬性的更改。它對應用程序中模型和控制器層之間的通訊特別有用。(在OS X中,控制器層綁定技術嚴重依賴於鍵值觀察。)控制器對象一般觀察模型對象的屬性,視圖對象經過控制器觀察模型對象的屬性。然而,另外,模型對象能夠觀察其餘模型對象(一般用於肯定從屬值什麼時候改變)或甚至自身(再次肯定從屬值什麼時候改變)。

先看下使用KVO的姿式atom

Xcode -> New -> MacOS -> CommandLine 新建工程,建立Person類設計

Person.hcode

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject

@property (nonatomic ,copy) NSString *name;

@property (nonatomic ,assign) NSUInteger age;

@property (nonatomic ,copy) NSArray<Person *> *friends;

@end

NS_ASSUME_NONNULL_END

Person.morm

#import "Person.h"

@implementation Person

- (instancetype)init {
    
    self = [super init];
    
    if (self) {
        _name = @"";
        _age = 0;
        _friends = @[];
    }
    
    return self;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    
    // 當收到通知的時候打印觀察的對象,舊值和新值
    NSLog(@"\nReceving ObserveValueChanged \nObject: %@ OldValue: %@, NewValue: %@",object, change[NSKeyValueChangeOldKey], change[NSKeyValueChangeNewKey]);
}

// 重寫以便打印對象的屬性
- (NSString *)description {
    
    return [NSString stringWithFormat:@"- name: %@, age: %ld, friends: %@",self.name, self.age, self.friends];
}

@end

打開main.m,建立Alice和Bob,設置Bob觀察Alice的age屬性server

#import <Foundation/Foundation.h>
#import "Person.h"

static void *PersonChangeContext = &PersonChangeContext;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        Person *Alice = Person.new;
        Alice.name = @"Alice";
        Alice.age = 18;
        
        Person *Bob = Person.new;
        Bob.name = @"Bob";
        Bob.age = 28;
        
        
        [Alice addObserver:Bob forKeyPath:@"age" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:PersonChangeContext];
        
        Alice.age = 100;
        
        [Alice removeObserver:Bob forKeyPath:@"age"];
        
        Alice.age = 200;
        
    }
    return 0;
}

能夠看到控制檯輸出了一次Alice的age屬性的先後變化.對象

其中,NSKeyValueObservingOptions 有如下幾個選項,可使用 | 符號組合使用繼承

  • NSKeyValueObservingOptionNew // 收到通知時change字典將包含新值
  • NSKeyValueObservingOptionOld // 收到通知時change字典將包含舊值
  • NSKeyValueObservingOptionInitial // 在addObserver時會發送通知,change字典將包含初始值
  • NSKeyValueObservingOptionPrior // 在所觀察keyPath改變以前將收到通知

change字典的key值在這裏

FOUNDATION_EXPORT NSKeyValueChangeKey const NSKeyValueChangeKindKey;
FOUNDATION_EXPORT NSKeyValueChangeKey const NSKeyValueChangeNewKey;
FOUNDATION_EXPORT NSKeyValueChangeKey const NSKeyValueChangeOldKey;
FOUNDATION_EXPORT NSKeyValueChangeKey const NSKeyValueChangeIndexesKey;
FOUNDATION_EXPORT NSKeyValueChangeKey const NSKeyValueChangeNotificationIsPriorKey

KVO的使用場景有不少,好比Person擁有一個account屬性,person須要獲取account的變化該如何處理呢,輪訓或許是個辦法,可是無疑效率低下,並且很不安全,更合理的辦法就是使用KVO觀察account,在account發生變化時更新。

原理

如今咱們已經知其然了,可是還不知其因此然。

先說結論

  1. 系統經過runtime生成繼承與被觀察者的新類(NSKVONotifying_Person),對原對象進行isa-swizzing(isa混寫)
  2. 根據KVC(鍵值編碼)對對象的keypath進行hock
  3. 在將要對屬性進行賦值操做時發送通知

如何證實上述結論呢,上代碼!

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "Person.h"

static void *PersonChangeContext = &PersonChangeContext;

int main(int argc, const char * argv[]) {
    @autoreleasepool {        
        
        Person *Alice = Person.new;
        Alice.name = @"Alice";
        Alice.age = 18;
        
        Person *Bob = Person.new;
        Bob.name = @"Bob";
        Bob.age = 28;
        
        
        Class cls0 = object_getClass(Alice);
        
        [Alice addObserver:Bob forKeyPath:@"age" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:PersonChangeContext];
        
        Alice.age = 100;
        
        Class cls1 = object_getClass(Alice);
        
        NSLog(@"%@ %@",cls0,cls1);
        
    }
    return 0;
}
  1. 首先引入runtime,這樣咱們能夠打印isa所指向的真實的類,
  2. 在向Alice添加觀察者以前,先獲取Class
  3. 在向Alice添加觀察者以後,獲取Class
Person NSKVONotifying_Person

能夠看到輸出確實如此

這就是KVO的核心思路了,關於KVC請看這裏.

相關文章
相關標籤/搜索