当前位置: 首页 > 科技观察

Objective-C的KVO简单探索了

时间:2023-03-11 23:49:52 科技观察

KVO(KeyValueObserving),也就是Foundation中Observer模式的实现。KVO的原理简而言之就是:1.当一个对象有观察者时,动态创建对象类的子类2.对于每一个观察到的属性,重写它的set方法3.在重写的set中调用-willChangeValueForKey:和-didChangeValueForKey:在通知观察者的方法中4.当一个属性没有观察者时,删除重写的方法5.当没有观察者观察任何属性时,删除动态创建的子类。简单验证一下。@interfaceSark:NSObject@property(nonatomic,copy)NSString*name;@end@implementationSark@endSark*sark=[Sarknew];//断点1[sarkaddObserver:selfforKeyPath:@"name"options:NSKeyValueObservingOptionNewcontext:nil];//breakpoint2sark.name=@"Sarksa";[sarkremoveObserver:selfforKeyPath:@"name"];//断点3使用-class和object_getClass()获取sark对象的Class和真正的Class//breakpoint1(lldb)posark。classSark(lldb)poobject_getClass(sark)Sark//breakpoint2(lldb)posark.classSark(lldb)poobject_getClass(sark)NSKVONotifying_Sark//breakpoint3(lldb)posark.classSark(lldb)poobject_getClass(sark)Sark,观察sark对象时,框架使用runtime动态创建了Sark类NSKVONotifying_Sark的子类,为了隐藏这种行为,NSKVONotifying_Sark被重写——类方法返回之前的类,就好像什么都没发生一样。但是在使用object_getClass()时暴露了,因为这个方法返回的是这个对象的isa指针,而这个指针必须指向这个对象的一个??类对象。那么我们就来看看这个动态类实现的方法吧。在这里,请找一个NSObjectExtendNSObject+DLIntrospection,里面封装了打印某个类的方法、属性、协议等常用的调试方法,一目了然。@interfaceNSObject(DLIntrospection)+(NSArray*)类;+(NSArray*)属性;+(NSArray*)实例变量;+(NSArray*)类方法;+(NSArray*)实例方法;+(NSArray*)协议;+(NSDictionary*)descriptionForProtocol:(Protocol*)proto;+(NSString*)parentClassHierarchy;@end然后在刚才的断点处继续调试://breakpoint1(lldb)po[object_getClass(sark)instanceMethods]<__NSArrayI0x8e9aa00>(-(void)setName:(id)arg0,-(void).cxx_destruct,-(id)name)//breakpoint2(lldb)po[object_getClass(sark)instanceMethods]<__NSArrayI0x8d55870>(-(void)setName:(id)arg0,-(class)class,-(void)dealloc,-(BOOL)_isKVOA)//断点3(lldb)po[object_getClass(sark)instanceMethods]<__NSArrayI0x8e9cff0>(-(void)setName:(id)arg0,-(void).cxx_destruct,-(id)name)首先有一个很吸引眼球的——.cxx_destruct,这是什么产品?详细探索可以参考我的另一篇文章。大概意思是arc下的这个方法负责在所有dealloc调用完成后释放所有变量。当然,这与KVO无关,言归正传。从上面breakpoint2的打印可以看出动态类重写了4个方法:1.-setName:最重要的重写方法,设置值时调用通知函数。2.-class隐藏自己,回到原来的classclass3,-dealloc清理案发现场4,-_isKVOA这个是内部使用的标记,判断这个类是否已经被KVO动态生成子类接下来验证KVO是否调用了-willChangeValueForKey:和-重写了set方法didChangeValueForKey:后最直接的验证方法就是在Sark类中重写这两个方法:@implementationSark-(void)willChangeValueForKey:(NSString*)key{NSLog(@"%@",NSStringFromSelector(_cmd));[superwillChangeValueForKey:key];}-(void)didChangeValueForKey:(NSString*)key{NSLog(@"%@",NSStringFromSelector(_cmd));[superdidChangeValueForKey:key];}@end