iOS KVC

作者: 天空像天空一样蓝 | 来源:发表于2019-09-28 20:54 被阅读0次

    KVC

    KVC的全称是Key-Value Coding,俗称“键值编码”,可以通过一个key来访问某个属性

    1、基础使用

    1.1、常用API

    - (void)setValue:(id)value forKey:(NSString *)key;
    - (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
    - (id)valueForKey:(NSString *)key;
    - (id)valueForKeyPath:(NSString *)keyPath;
    

    1.2 使用

    创建一个Person类和Cat

    @interface Cat : NSObject
    @property (assign, nonatomic) int weight;
    @end
    
    @interface Person : NSObject
    @property (assign, nonatomic) int age;
    
    @property (strong, nonatomic) Cat *cat;
    @end
    
    • 使用- (void)setValue:(id)value forKey:(NSString *)key;方法给属性赋值
    Person *person = [[Person alloc] init];
    
    [person setValue:@10 forKey:@"age"];
    NSLog(@"person的age == %d", person.age);
    打印结果:
    person的age == 10
    

    上面直接给age属性赋值很简单,但是怎么给Cat类里面的weight属性赋值呢?
    直接使用调用[person setValue:@99 forKey:@"cat.weight"]这个方法会报经典的系统错误*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Person 0x10051ed50> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key cat.weight.找不到这个key

    • 使用- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;不仅具有”forKey“的功能,还能给深层次的属性赋值
    // Cat 必须初始化一下
    person.cat = [[Cat alloc] init];
    [person setValue:@99 forKeyPath:@"cat.weight"];
    打印结果:
    cat的weight == 99
    
    • 使用- (id)valueForKey:(NSString *)key;获取属性值
    [person setValue:@10 forKey:@"age"];
    NSLog(@"person的age == %@", [person valueForKey:@"age"]);
    打印结果:
    person的age == 10
    
    • 使用- (id)valueForKeyPath:(NSString *)keyPath;获取属性值
    person.cat = [[Cat alloc] init];
    [person setValue:@99 forKeyPath:@"cat.weight"];
    NSLog(@"cat的weight == %@", [person valueForKeyPath:@"cat.weight"]);
    打印结果:
    cat的weight == 99
    

    2、底层原理

    2.1 setValue: forKey:的原理

    原理

    我们使用 setValue:forKey:的方法给Person类的age属性赋值。

    1. 先去Person类里面找有没有setAge:有的话传递参数,调用改该方法,否则进行第2步
    2. 找到_setAge:方法,有则传递参数,无进行第3步
    3. 找到accessInstanceVariablesDirectly 这个方法,返回一个Bool值
    • 返回为NO表示不允许访问成员变量,调用setValue:forUndefinedKey: 并抛出异常NSUnknownKeyException
    • 返回为YES则可以访问,进行第4步
    1. 会按照_key、_isKey、key、isKey的顺序进行赋值,有则直接赋值,无则进行第5步
    2. 依然会抛出调用setValue:forUndefinedKey: 并抛出异常NSUnknownKeyException这个信息

    2.2 valueForKey:的原理

    原理
    1. 按照getKey、key、iskey、_key的先后顺序查找方法,如果找到方法直接取值,否则进入第2步
    2. 找到accessInstanceVariablesDirectly 这个方法,返回一个Bool值
    • 返回为NO表示不允许访问成员变量,调用valueForUndefinedKey: 并抛出异常NSUnknownKeyException
    • 返回为YES按照顺序查找成员变量_key、_isKey、key、isKey,如果找到直接取值,否则进入第3步。
    1. 依然会抛出调用valueForUndefinedKey: 并抛出异常NSUnknownKeyException

    面试

    • 通过KVC修改属性会触发KVO吗?
      会。
      我们自定义一个Observer MyObserver
    #import <Foundation/Foundation.h>
    @interface MyObserver : NSObject
    @end
    
    #import "MyObserver.h"
    
    @implementation MyObserver
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
    {
        NSLog(@"observeValueForKeyPath - %@", change);
    }
    

    然后调用下面的方法,会发现,打印了上面代码里面的内容

    MyObserver *observer = [[MyObserver alloc] init];
    MyObserver *person = [[MyObserver alloc] init];
        
        // 添加KVO监听
    [person addObserver:observer forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
        
        // 通过KVC修改age属性
    [person setValue:@10 forKey:@"age"];
        // 移除KVO监听
    [person removeObserver:observer forKeyPath:@"age"];
    
    • KVC的赋值和取值过程是怎样么?原理是什么
      如 上面2.2的过程

    相关文章

      网友评论

        本文标题:iOS KVC

        本文链接:https://www.haomeiwen.com/subject/bjarrctx.html