KVO是什么
一下是摘自苹果官网的一段话
Key-value observing is a mechanism that allows objects to be notified of changes to specified properties of other objects
KVO其实就是可以让开发人员观察到对象特定属性变化的一种机制
重要一点:为了能理解KVO,首先要理解KVC。(因为KVC一定会触发KVO的,如果一个属性没有setXX方法,通过KVC改变属性,同样会触发KVO,所以KVO与属性的setXX方法没有必然联系)
苹果内部实现细节
Automatic key-value observing is implemented using a technique called isa-swizzling.
自动KVO是通过isa-swizzling技术来实现的,所以本文对KVO的实现是对自动KVO的实现,而且也正是苹果官网所说的isa-swizzling技术来实现
When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class. As a result the value of the isa pointer does not necessarily reflect the actual class of the instance.
当我们调用方法增加一个属性增加观察的时候,此时苹果系统会通过runtime生成一个原类的子类,名字为NSKVONotifying_原类的名字
;同时对象的isa被指向了runtime自己生成的类。通过以下代码可以获得证明
1 | _p = [[Person alloc] init]; |
1 | -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{ |
自己具体实现KVO
整个实现思路也主要就是上边的那些分析,通过runtime创建了一个子类,然后交换isa1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28NSString *setterStr = private_setterForKey(key);
Method setterMethod = class_getInstanceMethod(self.class, NSSelectorFromString(setterStr));
NSString *oldClassName = NSStringFromClass(self.class);
NSString *kvoClassName = [@"FOFKVO_" stringByAppendingString:oldClassName];
Class kvoClass;
kvoClass = objc_lookUpClass(kvoClassName.UTF8String);
if (!kvoClass) {
kvoClass = objc_allocateClassPair(self.class, kvoClassName.UTF8String, 0);
objc_registerClassPair(kvoClass);
}
if (setterMethod) {//直接调用setXX方法改变值
class_addMethod(kvoClass,NSSelectorFromString(setterStr), (IMP)setterIMP, "v@:@");//重写setXX方法
}else{//通过kvc改变值,通过method-swizzling
Method method1 = class_getInstanceMethod(self.class, @selector(setValue:forKey:));
Method method2 = class_getInstanceMethod(self.class, @selector(swizz_setValue:forKey:));
method_exchangeImplementations(method1, method2);
}
object_setClass(self, kvoClass);//isa-swizzling实例的isa已经指向我们新创建的
FOFObserverInfo *info = [[FOFObserverInfo alloc] initWithObserver:observer.description key:key block:block];
NSMutableArray *observers = objc_getAssociatedObject(self, kObservers);
if (!observers) {
observers = [NSMutableArray array];
objc_setAssociatedObject(self, kObservers, observers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
[observers addObject:info];
1 | #pragma mark - Private |
完整代码
资料
以下两篇苹果官方文档很详细,建议大家要想仔细研究KVO和KVC的话,最好先仔细看一下以下两篇文档,这两篇文章基本上够用了。因为网上一些非官方的文档都是自己的理解,不一定对。