通過在了解KVO的實現(xiàn)原理和實現(xiàn)步驟之后,我們可以手動實現(xiàn)KVO,具體可以看最后的demo,這里只講實現(xiàn)原理
添加觀察者大致分為三大步驟
- 動態(tài)創(chuàng)建一個子類:NSKVONotifying_<obj>
- isa指向剛創(chuàng)建的子類
- 關(guān)聯(lián)observer
如果要細分的話:
步驟一
1:利用runtime增加新的子類
NSString *oldName = NSStringFromClass([self class]);
NSString *newName = [[NSString alloc]initWithFormat:@"NSKVONotifying_%@",oldName];
//創(chuàng)建并注冊類
Class newClass = NSClassFromString(newName);
if (!newClass) {
newClass = objc_allocateClassPair([self class], newName.UTF8String, 0);
objc_registerClassPair(newClass);
}
2:在新的類中添加自定義的方法class,setter
//class
Method classMethod = class_getInstanceMethod([self class], @selector(class));
const char* classTypes = method_getTypeEncoding(classMethod);
class_addMethod(newClass, @selector(class), (IMP)wp_class, classTypes);
//setter方法
NSString *setterMethodName = setterForGeter(keyPath);
SEL setterSEL = NSSelectorFromString(setterMethodName);
Method setterMethod = class_getInstanceMethod([self class], setterSEL);
const char* setterTypes = method_getTypeEncoding(setterMethod);
class_addMethod(newClass, setterSEL, (IMP)wp_setter, setterTypes);
3:返回新的子類
步驟二:
1:使用runtime修改isa指向到步驟一返回的新類
object_setClass(self, newCLass);
步驟三:
1:使用runtime關(guān)聯(lián)observer
objc_setAssociatedObject(self, (__bridge void *)@"objc", observer, OBJC_ASSOCIATION_ASSIGN);
實現(xiàn)觀察者:
1:在自定義的setter方法中,向父類(需要注意的是:新的子類的父類是Person類)發(fā)送setter方法
objc_msgSendSuper(&superStruct, _cmd, newValue);//此時Person類中的setter方法打印了
2 . 通知觀察者,值發(fā)生改變了
id observer = objc_getAssociatedObject(self, (__bridge void *)@"objc");
NSString* setterName = NSStringFromSelector(_cmd);
NSString* key = getterForSetter(setterName);
objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:), key, self, @{key:newValue}, nil);
移除觀察者:
- 只需要修改isa指針即可:
Class superClass = class_getSuperclass(object_getClass(self));
object_setClass(self, superClass);
自動移除觀察者:
方法1. 在創(chuàng)建子類時hook系統(tǒng)方法dealloc
Method m1 = class_getInstanceMethod(object_getClass(self), NSSelectorFromString(@"delloc"));
Method m2 = class_getInstanceMethod(object_getClass(self), @selector(wp_delloc));
method_exchangeImplementations(m1, m2);
- (void)wp_delloc{
[self wp_delloc];
Class superClass = class_getSuperclass(object_getClass(self));
object_setClass(self, superClass);
}
方法2. 在創(chuàng)建子類時為新的子類添加dealloc方法
SEL deallocSEL = NSSelectorFromString(@"dealloc");
Method deallocMethod = class_getInstanceMethod([self class], deallocSEL);
const char* deallocTypes = method_getTypeEncoding(deallocMethod);
class_addMethod(newClass, deallocSEL, (IMP)my_dealloc, deallocTypes);
void my_dealloc(id self,SEL _cmd){
Class class = object_getClass(self);
Class superClass = class_getSuperclass(class);
object_setClass(self, superClass);
}
block實現(xiàn):
這里實現(xiàn)的比較簡單,也是探索吧,在category中新增一個數(shù)組保存模型,此模型保存了傳過來的observer,keyPath還有block
然后在setter方法中獲取數(shù)組并且判斷keyPath還有block是否存在,執(zhí)行block,不需要手動告訴觀察者值改變了,發(fā)送消息.
static void wp_setter(id self,SEL _cmd,id newValue){
NSLog(@"%s", __func__);
struct objc_super superStruct = {
self,
class_getSuperclass(object_getClass(self))
};
//使用block
NSString *keyPath = getterForSetter(NSStringFromSelector(_cmd));
//獲取舊值
id oldValue = objc_msgSendSuper(&superStruct, NSSelectorFromString(keyPath)); //kvc getter方法
//改變父類的值
objc_msgSendSuper(&superStruct, _cmd, newValue);//此時使用的Person中的setter方法打印了
NSMutableArray *array = objc_getAssociatedObject(self, WPKVOKey);
if (!array) {
return;
}
for (WPInfo *info in array) {
if ([info.keypath isEqualToString:keyPath] && info.block) {
info.block(info.observer, keyPath, oldValue, newValue);
return;
}
}
}
最后附上Demo地址:https://github.com/gnaw9258wp/KVO_Custom.git