一、常见问题
1. iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?)
利用RuntimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的子类。全新的类的 superclass 等于原来的类。
当修改instance对象的属性时,会调用Foundation的_NSSetXXXValueAndNotify函数
苹果还重写了类的 class 方法,从而达到隐藏类,通过 object_getClass 可以拿到真实类型。
- (void)_NSSetXXXValueAndNotify(id delegate){
[delegate willChangeValueForKey:key]];
[super 父类原来的 setter];
[delegate didChangeValueForKey:key];
// 内部会触发监听器(Oberser)的监听方法
observeValueForKeyPath:ofObject:change:context:
}
2. 如何手动触发KVO?
手动调用willChangeValueForKey:和didChangeValueForKey
3. 直接修改成员变量会触发KVO么?
不会触发KVO,因为没有用到 set方法。
4. 通过KVC修改属性会触发KVO么?
会触发KVO,因为底层内部会调用 willChangeValueForKey 和 didChangevalueForKey 方法。
5. 取消 KVO 方法
二、KVO 变化
未使用KVO监听的对象
使用了KVO监听的对象
三、KVC 原理
一个对象在调用setValue的时候
-
检查是否存在-、-is(只针对布尔值有效)或者-get的访问器方法,如果有可能,就是用这些方法返回值; 检查是否存在名为-set:的方法,并使用它做设置值。对于 -get和 -set:方法,将大写Key字符串的第一个字母,并与Cocoa的方法命名保持一致;
-
如果上述方法不可用,则检查名为-_、-_is(只针对布尔值有效)、-_set和-_get:方法;
-
如果没有找到访问器方法,可以尝试直接访问实例变量。实例变量可以是名为:或_;
-
如果仍未找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。这些方法的默认实现都是抛出异常,我们可以根据需要重写它们。
setValue:forKey 原理
- 首先搜索set:方法 (上面的setter方法没有找到,如果类方法accessInstanceVariablesDirectly 返回YES)
- 按_,_is,,is的顺序搜索成员名。
- 如果找到就设置成员名,如果没有就调用setValue:forUndefinedKey。
valueForKey 原理
按 get、、is的顺序查找getter方法,找到直接调用。如果是bool、int等内建值类型,会做NSNumber的转换。
四、KVC操作集合符
- @count:
- 返回一个值为集合中对象总数的NSNumber对象
- @sum:
- 首先把集合中的每个对象都转换为double类型,然后计算其总,最后返回一个值为这个总和的NSNumber对象
- @avg:
- 首先把集合中的每个对象都转换为double类型,然后计算其平均值,最后返回一个值为这个总和的NSNumber对象
- @max:
- 使用compare:方法来确定最大值。所以为了让其正常工作,集合中所有的对象都必须支持和另一个对象的比较
- @min:
- 和@max一样,但是返回的是集合中的最小值。
- 使用示例:
- products是数组,数组中存放了很多对象,每个对象都有一个price的属性。
- [products valueForKeyPath:@"@sum.price"];
- 可以用self作为操作符后面的keyPath来获取一个由NSNumber组成的数组或者集合的总值。
- [@[@1,@2] valueForKey:@"@max.self"];
五、KVC对象操作符
- @distinctUnionOfObjects:
- 获取数组中每个对象的属性的值,放到一个数组中并返回,会对数组去重。
- @unionOfObjects:
- 同@distinctUnionOfObjects,但是不去重。
- 使用示例:
Person *lilei = [[Person alloc] init];
lilei.name = @"LiLei";
Person *hanMeiMei = [[Person alloc] init];
hanMeiMei.name = @"hanMeiMei";
NSArray *array = @[lilei, hanMeiMei];
NSLog(@"array is %@",[array valueForKeyPath:@"@distinctUnionOfObjects.name"]);
输出结果为:
2015-11-10 16:08:07.977 TestPro[49746:521302] array is (
LiLei,
hanMeiMei
)
六、数组和集合操作符
- @distinctUnionOfArrays: 获取数组中每个数组中的每个对象的属性的值,放到一个数组中并返回,会对数组去重复。
- @unionOfArrays: 同@distinctUnionOfArrays,但是不去重。
- @distinctUnionOfSets: 获取集合中每个集合中的每个对象的属性的值,放到一个集合中并返回。
- 使用示例:
Person *lilei = [[Person alloc] init];
lilei.name = @"LiLei";
Person *hanMeiMei = [[Person alloc] init];
hanMeiMei.name = @"hanMeiMei";
NSArray *array = @[lilei, hanMeiMei];
NSLog(@"array is %@",[ @[array,array] valueForKeyPath:@"@unionOfArrays.name"]);
输出结果为:
2015-11-10 16:51:26.137 TestPro[50404:556930] array is (
LiLei,
hanMeiMei,
LiLei,
hanMeiMei
)