通过在了解KVO的实现原理和实现步骤之后,我们可以手动实现KVO,具体可以看最后的demo,这里只讲实现原理
添加观察者大致分为三大步骤
- 动态创建一个子类:NSKVONotifying_<obj>
- isa指向刚创建的子类
- 关联observer
如果要细分的话:
步骤一
1:利用runtime增加新的子类
NSString *oldName = NSStringFromClass([self class]);
NSString *newName = [[NSString alloc]initWithFormat:@"NSKVONotifying_%@",oldName];
//创建并注册类
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关联observer
objc_setAssociatedObject(self, (__bridge void *)@"objc", observer, OBJC_ASSOCIATION_ASSIGN);
实现观察者:
1:在自定义的setter方法中,向父类(需要注意的是:新的子类的父类是Person类)发送setter方法
objc_msgSendSuper(&superStruct, _cmd, newValue);//此时Person类中的setter方法打印了
2 . 通知观察者,值发生改变了
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. 在创建子类时hook系统方法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. 在创建子类时为新的子类添加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实现:
这里实现的比较简单,也是探索吧,在category中新增一个数组保存模型,此模型保存了传过来的observer,keyPath还有block
然后在setter方法中获取数组并且判断keyPath还有block是否存在,执行block,不需要手动告诉观察者值改变了,发送消息.
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
网友评论