原理
當(dāng)你觀察一個對象時,一個新的類會動態(tài)被創(chuàng)建。這個類繼承自該對象的原本的類,并重寫了被觀察屬性的 setter 方法。自然,重寫的 setter 方法會負(fù)責(zé)在調(diào)用原 setter方法之前和之后,通過調(diào)willChangeValueForKey: 和 didChangevlueForKey:這兩個方法來通知所有觀察對象值的更改。最后把這個對象的 isa 指針 指向這個新創(chuàng)建的子類,對象就神奇的變成了新創(chuàng)建的子類的實例。不僅如此,Apple 還重寫了 -class 方法,企圖欺騙我們這個類沒有變,就是原本那個類。

原理
1.使用姿勢 http://zhangbuhuai.com/understanding-kvo/
- 監(jiān)聽的時候傳入上下文來區(qū)分
- 移除的時候根據(jù)上下文來移除
- 為了避免取消訂閱時造成的crash,可以把取消訂閱代碼放在@try-@catch語句中
static void * zwContentSize = &zwContentSize;
- (void)viewDidLoad {
[super viewDidLoad];
// 1. subscribe
[_tableView addObserver:self
forKeyPath:NSStringFromSelector(@selector(contentSize))
options:NSKeyValueObservingOptionNew
context:zwContentSize];
}
// 2. responding
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if (context == zwContentSize) {
// configure view
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)dealloc {
@try {
// 3. unsubscribe
[_tableView removeObserver:self
forKeyPath:NSStringFromSelector(@selector(contentSize))
context:zwContentSize];
}
@catch (NSException *exception) {
}
}
2.如何自己實現(xiàn)KVO http://tech.glowing.com/cn/implement-kvo/
3.手動設(shè)定KVO https://yq.aliyun.com/articles/30483
如果將一個對象設(shè)定成屬性,這個屬性是自動支持KVO的,如果這個對象是一個實例變量,那么,這個KVO是需要我們自己來實現(xiàn)的
//
// Student.m
// SuperNotification
//
// Copyright (c) 2014年 Y.X. All rights reserved.
//
#import "Student.h"
@implementation Student
@synthesize name = _name;
- (void)setName:(NSString *)name
{
_name = name;
}
- (NSString *)name
{
return _name;
}
// 手動設(shè)定KVO
- (void)setAge:(NSString *)age
{
[self willChangeValueForKey:@"age"];
_age = age;
[self didChangeValueForKey:@"age"];
}
- (NSString *)age
{
return _age;
}
// 該方法來告訴系統(tǒng)是否監(jiān)聽某個key
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
{
// 如果監(jiān)測到鍵值為age,則指定為非自動監(jiān)聽對象
if ([key isEqualToString:@"age"])
{
return NO;
}
return [super automaticallyNotifiesObserversForKey:key];
}
@end