前言
這篇文章主要寫KVO的內(nèi)部通知
正文
先上代碼


經(jīng)測試,person.name = @"Jack"和[person setValue:@"Jack" forKey:@"name"]均可觸發(fā)KVO,而[person changeName:@"Jack"]不能。前兩種方式會訪問setter,第三種直接訪問成員變量,可以猜測KVO內(nèi)部是在setter中觸發(fā)的。
在Foundation/NSKeyValueObserving.h可以找到以下方法
- (void)willChangeValueForKey:(NSString *)key;
- (void)didChangeValueForKey:(NSString *)key;
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key;
+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key控制是否自動發(fā)送通知,如果返回NO,KVO無法自動運作,需手動觸發(fā)。因為前兩個方法默認是在setter中實現(xiàn)的(用KVO做鍵值觀察后,系統(tǒng)會在運行時重寫被觀察對象屬性的setter),即:
- (void)setName:(NSString *)name {
[self willChangeValueForKey:@"name"];
_name = name;
[self didChangeValueForKey:@"name"];
}
那么如果把這兩個方法移植到- (void)changeName:(NSString *)name中會怎樣?
- (void)changeName:(NSString *)name {
[self willChangeValueForKey:@"name"];
_name = name;
[self didChangeValueForKey:@"name"];
}

看吧,KVO又跑起來了。
再來看一下他們的調(diào)用順序

顯然和我們猜測的順序沒有出入,完全正確
這里重寫
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
沒有什么卵用,只是方便查看調(diào)用順序。因為auto這個方法只和setter相關,而現(xiàn)在是調(diào)用自定義方法并且內(nèi)部直接訪問成員變量。
至于內(nèi)部設置的那個name攔截,純屬為了娛樂??
自己出發(fā)KVO通知會生成中間類嗎?(關于中間類,且聽下回分解。。)當然會!
關于手動觸發(fā)的例子很多,比如主流三方框架SDWebImage中的SDWebImageDownloaderOperation.h和MJRefesh中的UIScrollView+MJRefresh.h,有興趣可以看下源碼,這里不做解析。
如果兩次設置的值相同會怎樣?

WTF!相同的值干嘛還要監(jiān)聽?把代碼改進一下
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
if ([key isEqualToString:@"name"]) {
return NO;
}
return [super automaticallyNotifiesObserversForKey:key];
}
- (void)setName:(NSString *)name {
if ([_name isEqualToString:name]) {
return;
}
[self willChangeValueForKey:@"name"];
_name = name;
[self didChangeValueForKey:@"name"];
}
再來看一下結(jié)果

多幾次嘗試,總會有意外的收獲。成文匆忙,有錯誤的地方還請指正、包涵。