【面试题】KVO 相关
KVO 的实现原理
以对 JQObject
对象的 foo
属性进行 KVO
监听为例:
KVO 会在运行时生成
NSKVONotifying_JQObject
类:NSKVONotifying_JQObject
类继承自JQObject
;- 并替换父类
JQObject
的setFoo:
方法实现为(Foundation __NSSetXXXValueAndNotify)
其中 XXX 为监听属性类型; - 然后重写
class
方法,返回JQObject
; - 重写
dealloc
方法,在对象销毁时做一些清理操作; - 重写
NSObject
的_isKVOA
方法,并返回 YES。
将监听对象的 isa 指针指向
NSKVONotifying_JQObject
类对象;将监听对象的 superclass 指针指向
JQObject
对象;
Foundation 框架中相关 __NSSetXXXValueAndNotify
方法:
Foundation 框架中 _isKVOA 实现(默认返回 NO ):
代码:
#import "ViewController.h"
#import <objc/runtime.h>
@interface JQObject : NSObject
@property (nonatomic, assign) int foo;
@end
@implementation JQObject
@end
@interface ViewController ()
@end
NSString *getMethods(Class cls) {
unsigned int count;
Method *methodList = class_copyMethodList(cls, &count);
NSMutableString *methodNames = @"".mutableCopy;
for (int i = 0; i < count; i++) {
Method method = methodList[i];
[methodNames appendFormat:@"%@, ", NSStringFromSelector(method_getName(method))];
}
free(methodList);
return methodNames;
}
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
JQObject *obj1 = [[JQObject alloc] init];
JQObject *obj2 = [[JQObject alloc] init];
obj1.foo = 10;
// 未添加 KVO 监听前
Class obj1Isa = object_getClass(obj1);
Class obj2Isa = object_getClass(obj2);
NSString *obj1Methods = getMethods(obj1Isa);
NSString *obj2Methods = getMethods(obj2Isa);
Class obj1SuperClass = class_getSuperclass(obj1Isa);
Class obj2SuperClass = class_getSuperclass(obj2Isa);
IMP obj1IMP = [obj1 methodForSelector:@selector(setFoo:)];
IMP obj2IMP = [obj2 methodForSelector:@selector(setFoo:)];
NSLog(@"\n\n未添加 KVO 监听前:\n"
"obj1 isa: %@\n"
"obj2 isa: %@\n"
"obj1 的实例方法:%@\n"
"obj2 的实例方法:%@\n"
"obj1 superclass: %@\n"
"obj2 superclass: %@\n"
"obj1 'setFoo:' 方法地址: %p\n"
"obj2 'setFoo:' 方法地址: %p\n"
, obj1Isa, obj2Isa, obj1Methods, obj2Methods, obj1SuperClass, obj2SuperClass, obj1IMP, obj2IMP);
// 添加 KVO 监听
[obj2 addObserver:self forKeyPath:@"foo" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
// 添加 KVO 监听后
obj1Isa = object_getClass(obj1);
obj2Isa = object_getClass(obj2);
obj1Methods = getMethods(obj1Isa);
obj2Methods = getMethods(obj2Isa);
obj1IMP = [obj1 methodForSelector:@selector(setFoo:)];
obj2IMP = [obj2 methodForSelector:@selector(setFoo:)];
obj1SuperClass = class_getSuperclass(obj1Isa);
obj2SuperClass = class_getSuperclass(obj2Isa);
NSLog(@"\n\n添加 KVO 监听之后:\n"
"obj1 isa: %@\n"
"obj2 isa: %@\n"
"obj1 的实例方法:%@\n"
"obj2 的实例方法:%@\n"
"obj1 superclass: %@\n"
"obj2 superclass: %@\n"
"obj1 'setFoo:' 方法地址: %p\n"
"obj2 'setFoo:' 方法地址: %p\n"
, obj1Isa, obj2Isa, obj1Methods, obj2Methods, obj1SuperClass, obj2SuperClass, obj1IMP, obj2IMP);
// 断点 LLDB 打印 obj2IMP:
// p obj2IMP;
// (IMP) $0 = 0x00007fff207bf79f (Foundation`_NSSetIntValueAndNotify)
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"%@: %@", keyPath, change);
}
@end
打印:
示例项目地址:
https://github.com/Coder-ZJQ/demos/tree/master/interview/oc-kvo-impl