iOS 底层系列 - KVC KVO

iOS 底层系列 - KVC KVO

IOS小彩虹2021-10-20 11:58:06340A+A-

一、常见问题

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,因为底层内部会调用 willChangeValueForKeydidChangevalueForKey 方法。

5. 取消 KVO 方法

二、KVO 变化

未使用KVO监听的对象

使用了KVO监听的对象

三、KVC 原理

一个对象在调用setValue的时候

  1. 检查是否存在-、-is(只针对布尔值有效)或者-get的访问器方法,如果有可能,就是用这些方法返回值;

检查是否存在名为-set:的方法,并使用它做设置值。对于 -get和 -set:方法,将大写Key字符串的第一个字母,并与Cocoa的方法命名保持一致;

  1. 如果上述方法不可用,则检查名为-_、-_is(只针对布尔值有效)、-_set和-_get:方法;

  2. 如果没有找到访问器方法,可以尝试直接访问实例变量。实例变量可以是名为:或_;

  3. 如果仍未找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。这些方法的默认实现都是抛出异常,我们可以根据需要重写它们。

setValue:forKey 原理

  1. 首先搜索set:方法

(上面的setter方法没有找到,如果类方法accessInstanceVariablesDirectly 返回YES) 2. 按_,_is,,is的顺序搜索成员名。 3. 如果找到就设置成员名,如果没有就调用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:521302array 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:556930array is (
    LiLei,
    hanMeiMei,
    LiLei,
    hanMeiMei
)

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示 | 支付宝红包
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1
本网站由 提供CDN/云存储服务

联系我们