KVO
KVO 的全程是 Key-Value Observing ,也就是
键值监听
一个对象增加了 KVO 监听,则它实例对象的 ISA 指向新的 NSKVONotifying_类对象(Runtime 动态创建的一个类,是原来类对象的子类
)。
如果没增加监听,则isa指向原来的类对象。
新的子类的set属性方法,会调用Foundation
框架中的_NSSetIntValueAndNotify()
(根据数据类型) 这个 C 语言方法,大致如下:
[self willChangeValueForKey:@"xxx"];
[super setAge:xxx];
[self didChangeValueForKey:@"xxx"];
调试相关
- 通过
methodForSelector
方法,可以得到某个方法的 IMP ,应该是 IMP 地址 - 通过断点,
p (IMP)xxxx
这种,可以得到具体实现
面试题
-
KVO 的本质是什么?
答:
利用 RuntimeAPI 动态生成一个子类,并且让 instance 对象的 isa 指向这个全新的子类。
当修改 instance 对象的属性时,会调用 Foundation 框架的 _NSSetXXXValueAndNotify 函数:内部会先调用 willChangeValueForKey: 然后调用父类原来的 setter 方法 然后再调用 didChangeValueForKey: 这个方法(didChangeValueForKey)内部会触发监听器(Observer)的监听方法(observerValueForKeyPath:ofObject:change:context:)
-
如何手动触发 KVO
答:
手动调用 willChangeValueforKey 以及 didChangeValueForKey 这两个方法(缺一不可,因为后面的那个方法会判断前面的方法有没有调用) -
直接修改成员变量会触发 KVO 吗?
答:
不会。因为 KVO 的本质就是新的子类重写了属性的 setter 方法,而直接修改变量值,不会走这个 setter 方法。
KVC
KVC 的全称是 Key-Value Coding, 俗称
键值编码
,可以通过一个 key 来访问某个属性。
常见的 KVC 的 API
– (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
通过 keyPath 可以设置多层对象中的属性值,比如下面这句: keyPath 是"a.b.c.age",那么修改的就是 a 对象中的 b 对象 中的 c 对象的 age 属性。
[a setValue:(id)@(10) forKeyPath:"b.c.age"]
– (void)setValue:(id)value forKey:(NSString *)key;
通过 key 可以设置对象中属性值,比如 key 传 "name",那么就可以修改对象的 name 属性值。如下:
[a setValue:(id)@"小明" forKey:@"name"]
– (id)valueForKeyPath:(NSString *)keyPath;
取值,使用方法同上
– (id)valueForKey:(NSString *)key;
取值,使用方法同上
KVC 的赋值和取值原理
setValueForKey 的原理 既设置值原理
- 首先,直接调用
setKey:
、_setKey:
方法,找到则调用。 - 如果没有找到方法,则查看
accessInstanceVariablesDirectly
的返回值(是否允许直接访问成员变量,默认是YES)如果返回 NO ,则调用setValue:forUndefineKey:
并抛出NSUnknownKeyException
的异常。 - 如果返回的是 YES ,则按照
_key
、_isKey
、key
、isKey
的顺序查找成员变量,找到就直接赋值。如果没有,则调用setValue:forUndefineKey:
并抛出NSUnknownKeyException
的异常。
getValueForKey 的原理 既取值原理
- 首先,按照
getKey
、key
、isKey
、_key
顺序查找方法,找到就直接调用取值。 - 如果没有找到方法,则查看
accessInstanceVariablesDirectly
的返回值(是否允许直接访问成员变量,默认是YES)如果返回 NO ,则调用setValue:forUndefineKey:
并抛出NSUnknownKeyException
的异常。 - 如果返回的是 YES ,则按照
_key
、_isKey
、key
、isKey
的顺序查找成员变量,找到就直接取值。如果没有,则调用setValue:forUndefineKey:
并抛出NSUnknownKeyException
的异常。
面试题
-
通过 KVC 修改属性会触发 KVO 吗?
答:
会触发 KVO 。相当于 KVC 内部自动调用了 willChangeValueForKey 以及 didChangeValueForKey 这2个方法,触发 -
KVC 的赋值和取值过程是怎样的?原理是什么?
答:
参照上方赋值、取值原理。