1 KVC
KVC(Key-value coding)键值编码。简单来说,是可以通过对象属性名称(Key)直接给属性值(value)赋值。
1.1 使用
@property (class, readonly) BOOL accessInstanceVariablesDirectly; // 是否禁用KVC
- (nullable id)valueForKey:(NSString *)key; // getter
- (void)setValue:(nullable id)value forKey:(NSString *)key; // setter
通过 setter 方法我们就可以动态给 readonly 的对象赋值。key 可以是属性也可以是_属性。
1.2 底层调用
假如我们调用 [[NSObject alloc] setValue:nil forKey:@"property"];
,其 KVC 调用如下所示:
- 去模型中查找有没有对应的 setter 方法:例如:setProperty 方法,有就直接调用这个 setter 方法给属性赋值;
- 如果找不到 setter 方法,接着就会去寻找有没有 property Ivar,如果有,就直接进行
void object_setIvar ( id obj, Ivar ivar, id value )
赋值; - 如果找不到 property 属性,接着又会去寻找 _property Ivar,如果有,直接进行 Ivar 赋值
- 如果都找不到会报出如下所示的崩溃信息。
从崩溃信息我们可以发现如下信息
- KVC 使用了 OSSpinLock 锁
- 其存储信息可分散在 CFSetCreateMutable -> CFHash -> CFSetGetValue -> CFSetAddValue
2 KVO
Key-Value Observing (KVO) 建立在 KVC 之上,它通过重写 KVC 和监听 setter 方法,向外发送通知。
2.1 使用
// 1. 注册观察者,实施监听
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
// 2. 在回调方法中处理属性发生的变化
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (context == <#context#>) {
<#code to be executed upon observing keypath#>
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
// 3. 移除观察者
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context NS_AVAILABLE(10_7, 5_0);
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
当父类和子类同时 KVO 同一个对象时,在
dealloc
移除引起崩溃时 应对 context 赋值,移除时也应通过(void)removeObserver: forKeyPath: context:
方法移除。
2.2 底层实现
通过上面两张图的对比,我们发现对 test 执行 addObserver
操作时,test 的 isa 指向了NSKVONotifying_KVOTest。执行 removeObserver
操作时,其 isa 再次指回了 KVOTest。这也就是 isa-swizzling 技术,isa-swizzling 就是类型混合指针机制。
由此我们可以结合 runtime 得出如下结论。
- KVO 的底层是 runtime 编译时动态生成 NSKVONotifying_Class 对象。
- NSKVONotifying_Class 是一个动态类,其内部继承了 NSKVONotifying 对象。
- NSKVONotifying 内实现了如下操作:
- 重写了 KVC 的机制,这样调用
setValue: forKey:
时,外部 Observer 也能接到通知。 - 绑定了原 isa 便于
removeObserver
时,修改原始对象的 isa; - 通过动态方法决议与消息转发实现了属性的 setter 方法。
- 可以通过 NSObject 内的 NSKeyValueObserverNotification 扩展方法向外发送 NSKeyValueObservingOptions 通知。如下所示
- 重写了 KVC 的机制,这样调用
[test willChangeValueForKey:@"str"]; // KVO 存储旧值
test -> _str = @"阳君"; // 指针改变值
[test didChangeValueForKey:@"str"]; // KVO 存储新值,且发出通知
willChangeValueForKey:
和didChangeValueForKey:
成对出现缺一不可。
Appendix
Related Documentation
Revision History
时间 | 描述 |
---|---|
2017-03-22 | 博文完成 |
Copyright
CSDN:http://blog.csdn.net/y550918116j
GitHub:https://github.com/937447974