KVO和KVC
KVC:
-
KVC(Key-value coding)键值编码,就是指iOS的开发中,可以允许开发者通过key直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存储方法。这样就可以在运行时动态地访问和修改对象的属性。
- 在kvc当中有常用的4种方法:
- (nullable id)valueForKey:(NSString *)key; //直接通过Key来取值
- (void)setValue:(nullable id)value forKey:(NSString *)key; //通过Key来设值
- (nullable id)valueForKeyPath:(NSString *)keyPath; //通过KeyPath来取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath; //通过KeyPath来设值
1)kvc设值
在kvc设值是,要指出设值的属性,kvc需要在对象的内部来查找属性,这个流程的顺序为:
1.首先寻找set函数,先找setKey函数,如果不存在,就去寻找_setKey函数。
2.如果没有找到1中所述的函数,KVC机制会检查+ (BOOL)accessInstanceVariablesDirectly方法有没有返回YES,默认该方法会返回YES,当然也可以重写这个函数,让它返回NO,如果返回NO,会立即调用setValue:forUndefinedKey:方法,不过一般不会这样做,因为kvc可以直接向属性赋值而不通过set函数去实现,即不管是public修饰的还是private修饰,都可以直接赋值。
3.当开始直接查找属性时,KVC会按照 _Key,_isKey,Key,isKey, 这样的优先级去赋值;
4.当上述的方法和属性都不存在是,会调用setValue:forUndefinedKey:方法。此方法默认是抛出异常。
2)kvc取值
首先会按照getKey,Key,isKey来进行getter方法查找,若存在就赋值;
如果getter方法都没有找到,KVC机制就会查找countOfKey 、objectInKeyAtIndex 、KeyAtindex、getKey :range 这些方法,若上面的几个方法任一个被找到,那么就会返回一个NSKeyValueArray(含有所有的方法的代理集合),以上面的几个方法组合形式调用;
如果上面的countOfKey 、objectInKeyAtIndex 、KeyAtindex、getKey :range 这些方法没有找到 ,会去查找 countOfKey、enumeratorOfKey、memberOfKey格式的方法,若找到这三个方法会返回一个可以相应NSSet所有方法的代理集合,会以countOfKey、enumeratorOfKey、memberOfKey组合形式调用;
如果都没有找到,就去查找+ (BOOL)accessInstanceVariablesDirectly,如果返回YES,就和设值一样的执行机制。直接查找属性,按照_key,_iskey,key,iskey的顺序搜索成员。
KVC使用keypath
当属性为自己定义的类或者复杂的数据类型时,我们可以先获得这个成员,然后再次用KVC访问该成员的属性。但是这样比较繁琐,所以为了方便,我们可以使用 键路径,即KeyPath。
KVC处理异常
KVC中最常见的异常就是不小心使用了错误的key,或者在设值中不小心传递了nil的值,KVC中有专门的方法来处理这些异常。
通常情况下,KVC不允许你要在调用setValue:属性值 forKey:(或者keyPath)时对非对象传递一个nil的值。很简单,因为值类型是不能为nil的。如果你不小心传了,KVC会调用setNilValueForKey:方法。这个方法默认是抛出异常,所以一般而言最好还是重写这个方法。
KVC处理集合
简单集合运算符
简单集合运算符共有@avg, @count , @max , @min ,@sum5种
Person *p1=[[Person alloc ] init];
p1->_age=10;
Person *p2=[[Person alloc ] init];
p2->_age=10;
Person *p3=[[Person alloc ] init];
p3->_age=10;
Person *p4=[[Person alloc ] init];
p4->_age=10;
Person *p5=[[Person alloc ] init];
p5->_age=60;
NSArray *arr=@[p1,p2,p3,p4,p5];
NSNumber* sum = [arr valueForKeyPath:@"@sum.age"];
NSLog(@"%@",sum);
NSNumber* avg = [arr valueForKeyPath:@"@avg.age"];
NSLog(@"%@",avg);
NSNumber* count = [arr valueForKeyPath:@"@count"];
NSLog(@"%@",count);
2022-05-03 20:49:22.287921+0800 A7d[2483:85475] 100
2022-05-03 20:49:22.288128+0800 A7d[2483:85475] 20
2022-05-03 20:49:22.288594+0800 A7d[2483:85475] 5
KVO:
KVO(key-value observe)是在KVC的基础上实现的一种用于监听属性变化的设计模式;如果对某个类的某个属性设置了KVO,那么当这个属性发生变化时,就会触发监听方法,从而知道属性变化了。如果本类一个属性的改变会影响到其他多个属性的变化,我们也会经常自己重写这个属性的set方法,用来监听他的变化,但是如果不是本类的属性,我们就没办法重写其set方法了,这个时候KVO就可以上场了,其实KVO本质上也是重写set方法,而整个过程依赖于runtime才能实现。
1.添加监听者
[p1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld context:nil];
p1 被监听者
self 监听者
options 枚举类型,选择要保留的数据,本例保存了旧数据。默认保留了新数据
2.实现监听回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
NSLog(@"改变,,,%@",change[@"old"]);
}
当被监听的属性被改变时,会自动调用回调方法
3、实现原理
KVO是基于runtime实现的,在添加监听者时,runtime会动态生成一个类,此时实例对象的isa指针不在指向Person类对象,而是指向生成的类。并重写我们监听的属性(keyPath)的set方法
同时也重写了class函数,此时调用class仍然会返回Person类对象,而用object_getclass 会返回此时isa指向的类,即动态生成的类。
Person *p1=[[Person alloc ] init];
Person *p2=[[Person alloc ] init];
Class cls1 =object_getClass(p1);
Class cls2 =object_getClass(p2);
NSLog(@"添加监听前%@ %@",cls1,cls2);
//监听p1
[p1 addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionOld context:nil];
//改变p1的属性
[p1 setValue:@"jack" forKey:@"name"];
cls1 =object_getClass(p1);
cls2 =object_getClass(p2);
NSLog(@"添加监听后%@ %@",cls1,cls2);
2022-05-03 22:12:31.992420+0800 A7d[3280:137055] 添加监听前Person Person
2022-05-03 22:12:31.992831+0800 A7d[3280:137055] 改变,,,
2022-05-03 22:12:31.992964+0800 A7d[3280:137055] 添加监听后NSKVONotifying_Person Person
KVO是在KVC的基础上实现的,所以只有通过kvc来修改属性的值才会触发kvo,自己直接改变属性值则不会触发。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库