什么是KVO
- KVO是Key-Value Observing的首字母縮寫(xiě)
- KVO是Object-C對(duì)觀察者設(shè)計(jì)模式的實(shí)現(xiàn)
- Apple使用了isa混寫(xiě)(isa-swizzling)來(lái)實(shí)現(xiàn)KVO
KVO 提供一種機(jī)制浇辜,指定一個(gè)被觀察對(duì)象(例如 A 類(lèi))碧绞,當(dāng)對(duì)象某個(gè)屬性(例如 A 中的字符串 name)發(fā)生更改時(shí)豁翎,對(duì)象會(huì)獲得通知漓摩,并作出相應(yīng)處理;【且不需要給被觀察的對(duì)象添加任何額外代碼膊爪,就能使用 KVO 機(jī)制】
用一張圖來(lái)描述一下KVO的實(shí)現(xiàn)機(jī)制
上圖可以看出,注冊(cè)一個(gè)對(duì)象的觀察者的時(shí)候,實(shí)際上是調(diào)用了系統(tǒng)的
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
這個(gè)方法,調(diào)用這個(gè)方法后觀察者觀察對(duì)象A中的某個(gè)屬性,然后系統(tǒng)會(huì)在運(yùn)行時(shí)動(dòng)態(tài)的創(chuàng)建一個(gè)NSKVONotifying_A的這么樣一個(gè)類(lèi),原來(lái)的對(duì)象A的isa指針重新指向了NSKVONotifying_A這個(gè)類(lèi),把isa的指向進(jìn)行修改就是isa混寫(xiě)技術(shù).NSKVONotifying_A是類(lèi)A的子類(lèi),并重寫(xiě)了其中的Setter方法,通過(guò)對(duì)Setter方法的重寫(xiě)達(dá)到可以通知所有觀察者的目的.接下來(lái),在XCode工程當(dāng)中,來(lái)實(shí)際通過(guò)Setter方法的設(shè)置,KVO的監(jiān)聽(tīng)來(lái)感受一下KVO的實(shí)現(xiàn).
創(chuàng)建兩個(gè)文件,MyObject和MyObserver.
- MyObject
@interface MyObject : NSObject
@property (nonatomic,assign) int value;
-(void)increase;
@end
@implementation MyObject
-(instancetype)init
{
self = [super init];
if (self) {
_value = 0;
}
return self;
}
-(void)increase
{
_value += 1;
}
@end
- MyObserver
@implementation MyObserver
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSNumber *valueNum = [change valueForKey:NSKeyValueChangeNewKey];
NSLog(@"value is %@",valueNum);
}
@end
然后在AppDelegate中進(jìn)行KVO的監(jiān)聽(tīng)
- AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
MyObject *obj = [[MyObject alloc]init];
MyObserver *observer = [[MyObserver alloc]init];
//調(diào)用KVO方法監(jiān)聽(tīng)obj的value屬性的變化
[obj addObserver:observer forKeyPath:@"value" options:NSKeyValueObservingOptionNew context:NULL];
obj.value = 1;
return YES;
}
可以看到控制臺(tái)打印出了結(jié)果
說(shuō)明監(jiān)聽(tīng)成功了.
在obj.value那里打個(gè)斷點(diǎn),看看MyObject是怎么被改寫(xiě)的.
不出所料,在監(jiān)聽(tīng)的屬性Value被改寫(xiě)后,MyObject變成了NSKVONotifying_MyObject了.
為什么調(diào)用Setter方法就可以實(shí)現(xiàn)這種KVO的監(jiān)聽(tīng)呢.
重寫(xiě)的Setter添加的方法
- -(void)willChangeValueForKey:(NSString *)key
- -(void)didChangeValueForKey:(NSString *)key
那么在NSKVONotifying_MyObject中的Setter方法就變成了下面這樣
-(void)setValue:(id)obj
{
[self willChangeValueForKey:@"keyPath"];
//調(diào)用父類(lèi),也就是原類(lèi)的實(shí)現(xiàn)
[super setValue:obj];
[self didChangeValueForKey:@"keyPath"];
}
接下來(lái)有兩個(gè)問(wèn)題.
1.通過(guò)KVC設(shè)置Value能否生效
這個(gè)問(wèn)題用代碼來(lái)驗(yàn)證一下就可以了,如下所示
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
MyObject *obj = [[MyObject alloc]init];
MyObserver *observer = [[MyObserver alloc]init];
//調(diào)用KVO方法監(jiān)聽(tīng)obj的value屬性的變化
[obj addObserver:observer forKeyPath:@"value" options:NSKeyValueObservingOptionNew context:NULL];
obj.value = 1;
// 使用kvc來(lái)改變value的值
[obj setValue:@2 forKey:@"value"];
return YES;
}
結(jié)果控制臺(tái)打印如下:
說(shuō)明使用KVC設(shè)置屬性的方式是可以出發(fā)KVO的,說(shuō)明KVC設(shè)置屬性是觸發(fā)了Setter方法
2.使用成員變量賦值會(huì)出發(fā)KVO嗎
我們?cè)贏ppDelegate調(diào)用obj的increase方法,發(fā)現(xiàn)控制臺(tái)只打印了value is 1,說(shuō)明對(duì)成員變量賦值不會(huì)觸發(fā)KVO,但對(duì)increate方法進(jìn)行以下操作就不一樣了.
不過(guò)如果我們把increase方法變成下面這樣,再運(yùn)行試試
-(void)increase
{
[self willChangeValueForKey:@"value"];
_value += 1;
[self didChangeValueForKey:@"value"];
}
發(fā)現(xiàn)又觸發(fā)了KVO
所以根據(jù)上面的實(shí)驗(yàn)總結(jié)出下面幾點(diǎn)
- 使用setter方法改變值KVO才會(huì)生效
- 使用setValue:forKey:改變KVO才會(huì)生效
- 成員變量直接修改需手動(dòng)添加KVO才會(huì)生效
KVC
KVC是Key-Value coding的縮寫(xiě),也就是鍵值編碼,和鍵值編碼相關(guān)的兩個(gè)方法就是下面這兩個(gè)
- -(id)valueForKey:(NSString *)key
這個(gè)可以調(diào)用某個(gè)實(shí)例的ValueForKey:方法,來(lái)獲取和key同名或相似名稱(chēng)的實(shí)例變量的值 - -(void)setValue forKey:(NSString *)key
根據(jù)這個(gè)方法可以設(shè)置某一個(gè)對(duì)象和這個(gè)key同名或者相似名稱(chēng)的實(shí)例變量的值.