語言描述
KVO 的實質. ---- 官方文檔上面對于 KVO 實現調用的描述,也稱作,調用了 runtime 的isa swizzling
.實質上是將類進行過了替換.
假設我們定義了一個Person
類,包含有name
這個屬性
@interface Person:NSObject
@property (nonatomic,copy) NSString *name;
@end
在ViewController
中添加觀察者,觀察name
值的變化.
系統會動態的創建一個Person
的子類 NSKVONotifying_Person
. 并重寫name
的setter
方法
[self willChangeValueForKey:@"name"]; // 調用 observer 告知改變舊值
[self setName:name];
[self didChangeValueForKey:@"name"]; // 調用 observer 告知改變成新值
重寫之后,用這個類去替換原先的Person
類.這樣,在 Person
調用 person.name = @"someName";
時,KVO 操作對應的內容就會被調用.
Hank 公開課筆記中的內容
關于自定義 KVO
- 首先,要動態創建一個
Person
類的子類.
-(void)FF_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
/*
1.動態添加一個類
*/
NSString * oldClassName = NSStringFromClass([self class]);
NSString * newClassName = [@"MyKVO_" stringByAppendingString:oldClassName];
const char * newName = [newClassName UTF8String];
//定義一個類
Class subClass = objc_allocateClassPair([self class], newName, 0); // 動態創建一個類,繼承于[self Class],類名為 newName
//注冊這個類
objc_registerClassPair(subClass);
//改變isa指針!! 讓方法調用者,調用到當前的自定義方法.(可以看做原先 isa 指向 person 類,現在讓他指向 subClass)
object_setClass(self, subClass);
//重寫setAge方法!!
/*
說明, 我們平日里,在子類中調用方法,(假設 A有一個方法 methodA, a 是 A 的子類, a 是可以去調用 methodA),實質上是先在 SEL 列表中查找這個 methodA 方法,如果找不到, 繼續去尋找他的父類 A 的 SEL 中是否包含有這個方法.找到后去調用.
因此,實際上子類本身是不包含 `setAge`這個方法的.如果直接調用,那么實質上去調用父類的這個方法,這不是我們想要的.因為這里我們需要去重寫這個`setAge`的方法.
*/
class_addMethod(subClass, @selector(setAge:), (IMP)setAge, "v@:@");
}
//有默認參數!! RAC
void setAge(id self,SEL _cmd,int age){
NSLog(@"進入");
id class = [self class];
//讓自己指向父類
object_setClass(self, class_getSuperclass([self class]));
objc_msgSend(self, @selector(setAge:),age);
object_setClass(self, class);
}