从官方文档来武装一下自己(游击队->正规军)
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.
而这个中间类和 isa 就是 KVO 的核心了
KVO的核心在于,属性的改变的时候,可以兼容的到
我们一般在更改属性值的时候一般都是
对于直接赋值,这个属于直接修改了指针的指向,这个就很难抓到了
所以重点看 setter 方法 和 setValueForKey 这两个方法
于是 KVO 就要在运行的时候动态的兼容我们的 setter 方法
那么苹果的实现方法就是,运行时新建了一个被观察的对象的子类
将被观察对象的 isa 指针指向子类
isa 略作说明
每个对象都有 isa 指针,isa 指针存储了一个类所有的信息。
例如:所有的方法、所有的属性
换句话来说,我们对 OC 类的操作,底层都是对 isa 指针的操作
例如: isKindOf 方法
其源码可以看出
对象 -> isa -> isa_class -> isa_super_class 直到找到 isa 类型相等
没有找到则 return false,找到则 return true
子类重写了 setter 方法,然后就可以抓到属性的改变了
至于怎么通知给观察者改变的,看下面的 官方文档重点翻译
if your objects inherit from NSObject and you create properties in the usual way, your objects and their properties will automatically be KVO Compliant. It is also possible to implement compliance manually
Not all classes are KVO-compliant for all properties
Manual change notification provides additional control over when notifications are emitted, and requires additional coding. You can control automatic notifications for properties of your subclass by implementing the class method [automaticallyNotifiesObserversForKey:](https://developer.apple.com/documentation/objectivec/nsobject/1409370-automaticallynotifiesobservers)
.
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
BOOL automatic = NO;
if ([theKey isEqualToString:@"balance"]) {
automatic = NO;
}
else {
automatic = [super automaticallyNotifiesObserversForKey:theKey];
}
return automatic;
}
// Call the accessor method.
[account setName:@"Savings"];
// Use setValue:forKey:.
[account setValue:@"Savings" forKey:@"name"];
// Use a key path, where 'account' is a kvc-compliant property of 'document'.
[document setValue:@"Savings" forKeyPath:@"account.name"];
// Use mutableArrayValueForKey: to retrieve a relationship proxy object.
Transaction *newTransaction = <#Create a new transaction for the account#>;
NSMutableArray *transactions = [account mutableArrayValueForKey:@"transactions"];
[transactions addObject:newTransaction];
- (void)setBalance:(double)theBalance {
[self willChangeValueForKey:@"balance"];
_balance = theBalance;
[self didChangeValueForKey:@"balance"];
}
- (void)removeTransactionsAtIndexes:(NSIndexSet *)indexes {
[self willChange:NSKeyValueChangeRemoval
valuesAtIndexes:indexes forKey:@"transactions"];
// Remove the transaction objects at the specified indexes.
[self didChange:NSKeyValueChangeRemoval
valuesAtIndexes:indexes forKey:@"transactions"];
}