KVC详解

作者: 雪域红鹰 | 来源:发表于2020-04-15 22:16 被阅读0次

    kvc是什么?

    kvc(Key-value-coding)键值编码,一个非正式的协议.提供一种机制间接访问对象的属性.而不是通过调用setter,getter方法获取.就是说在ios开发中,可以允许开发者通过key名直接访问对象的属性,或者给对象的属性赋值.而不需要调用明确存取方法.

    kvc有什么作用?

    kvc的作用就是可以给对象的私有变量进行赋值.即在运行时动态的访问或修改对象的属性.

    kvc的赋值和取值

    kvc中定义都是对NSObject的扩展类实现的,oc中的kvc最基本的几个方法:

    • (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来设值

    NSKeyValueCoding类别中其他的一些方法

    • (BOOL)accessInstanceVariablesDirectly;
      //默认返回YES,表示如果没有找到Set<Key>方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员,设置成NO就不这样搜索
    • (BOOL)validateValue:(inout id __nullable * _nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
      //KVC提供属性值正确性验证的API,它可以用来检查set的值是否正确、为不正确的值做一个替换值或者拒绝设置新值并返回错误原因.
    • (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
      //这是集合操作的API,里面还有一系列这样的API,如果属性是一个NSMutableArray,那么可以用这个方法来返回.
    • (nullable id)valueForUndefinedKey:(NSString *)key;
      //如果Key不存在,且没有KVC无法搜索到任何和Key有关的字段或者属性,则会调用这个方法,默认是抛出异常
    • (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
      //和上一个方法一样,但这个方法是设值.
    • (void)setNilValueForKey:(NSString *)key;
      //如果你在SetValue方法时面给Value传nil,则会调用这个方法
    • (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
      //输入一组key,返回该组key对应的Value,再转成字典返回,用于将Model转到字典.

    kvc设值

    设值是通过setValue:forKey:的默认实现,给定输入参数value和key。试图在接收调用对象的内部,设置属性名为key的value,通过下面的步骤
    1.查找set<Key>、_ set<Key>命名的setter,按照这个顺序,如果找到的话,调用这个方法并将值传进去(根据需要进行对象转换).
    2.如果没有发现一个简单的setter,但是accessInstanceVariablesDirectly类属性返回YES,则查找一个命名规则为__<key>、_is<Key>、<key>、is<Key>的实例变量.根据这个顺序,如果发现则将value赋值给实例变量.
    3.如果没有发现setter或实例变量,则调用setValue:forUndefinedKey:方法,并默认提出一个异常,但是一个NSObject的子类可以提出合适的行为
    eg:

       @interface Person : NSObject{
          NSString *_name;
       }  
      @end
    
    //创建对象
    Person *p = [[Person alloc] init];
    //通过kvc赋值
    [p setValue:@"小明" forKey:@"name"];
    //通过kvc取值
    NSLog(@"p的名字是%@",[p valueForKey:@"name"]);
    
    //打印结果
    //p的名字是小明
    

    在以上代码中添加,accessInstanceVariablesDirectly为NO,结果如下.

     @implementation Person
     +(BOOL)accessInstanceVariablesDirectly{
         return NO;
     }
     -(id)valueForUndefinedKey:(NSString *)key{
       NSLog(@"出现异常,此key不存在%@",key);
       return nil;
     }
     -setValue:(id)value forUndefinedKey:(NSString *)key{
         NSLog(@"设置值时出现异常,该key不存在%@",key);
     }
     @end
      //打印结果
      设置值时出现异常,该key不存在name
      出现异常,此key不存在name
      p的名字是(null)
    

    如果accessInstanceVariablesDirectly为YES,打印结果和首次显示一样.这里不在重复结果.在修改成员变量名为_name,_isName,name,isName时都会打印出正确的结果.所以简单来说就是*如果没有找到set<key>,会按照_key,_isKey,key,isKey的顺序搜索成员并进行赋值操作.

    kvc取值

    取值是同valueForKey:来实现的,给定一个key当做输入参数,下面的步骤,在这个接收valueForKey:方法调用的类内部进行操作.
    1.通过getter方法搜索实例,例如get<Key>, <key>, is<Key>, _ <key>的拼接方案。按照这个顺序,如果发现符合的方法,就调用对应的方法并拿着结果
    2.如果没有找到上面getter方法,kvc会查找countOf<Key>
    objectIn<Key>AtIndex、<key>AtIndexes格式的方法.如果这个三个方法倍找到,那么返回一个可以响应NSArray所有方法的代理集合(NSKeyValueArray,是NSArray的子类),给这个代理集合发NSSet的消息,就会以这个几个方法组合的形式调用
    3.如果上面的方法名没有找到,那么会同时查找countOf<Key>,enumeratorOf<Key>,memberOf<Key>格式的方法,如果这三个方式都找到,那么返回一个可以响应NSSet所有的方法的代理集合,给这个代理集合发NSSet的消息,就会以这个几个方法组合的形式调用
    4.如果还没有找到,再检查类方法 accessInstanceVariablesDirectly是否为YES,如果是YES,会按_ <key>、_is<key>、<key>、is<key>的顺序搜索成员变量名,如果发现将value赋值给实例变量.如果accessInstanceVariablesDirectly为NO,那么会直接调用forUndefinedKey方法,抛出异常
    eg:

        @implementation Person
         -(NSInteger)getAge{
          return 10;
        }
       @end
    
    //创建对象
    Person *p = [[Person alloc] init];
    //通过kvc取值
    NSLog(@"p的年龄是%@",[p valueForKey:@"age"]);
    
    //打印结果
    //p的年龄是10
    

    从上面的代码可以看到,找到getAge方法,就获取到age的值.
    分别把getAge方法名改为age、_age、isAge都可以获取到正确的值.上面代码充分说明了kvc在调用valueforkey是搜索key的机制

    kvc中keyPath的使用

    在开发过程中,一个类的成员变量有可能是自定义类或者其他的复杂数据类型,那么可以使用kvc获取该属性.然后再用kvc来获取这个自定义类的属性.但是这个比较繁琐,所以,kvc提供一个解决方案,那就是键路径keypath

    • (nullable id)valueForKeyPath:(NSString *)keyPath; //通过KeyPath来
      取值
    • (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath; //通过
      KeyPath来设值

    eg:

    @interface Person : NSObject{ 
      NSString *_name;
    }
    @end
    
    @interface SubPerson : NSObject{
        Person *_person;
    }
    @end
    
    //创建对象Person
    Person *p = [[Person alloc] init];
    //创建对象SubPerson
    SubPerson *subp = [[SubPerson alloc] init];
    //通过kvc设置person 值
    [subp setValue:p forKey:@"person"];
    
    //通过kvc设值person的给name设值
    [subp setValue:@"小花" forKeyPath:@"person.name"];
    //通过kvc取值
    NSLog(@"SubPerson的person名字是%@",[p valueForKey:@"name"]);
    
    //打印结果
    //SubPerson的person名字是小花
    

    从上面例子可以看出我们成功的使用了keypath设置person的name属性值.

    kvc异常处理

    kvc中最常见的异常就是不小心使用了错误的key,或者设置值不小心传递了nil值.kvc中有专门的方法来处理这些异常

    kvc处理nil异常

    kvc中通常情况下是不能传递nil的,如果不小心传递了nil,kvc会调用setNilValueForkey方法,这个方法默认是抛出异常.
    eg:

    @interface Person : NSObject{
        NSInteger age;
    }
    @end
    
    //创建对象Person
    Person *p = [[Person alloc] init];
    //通过kvc设置age 值
    [p setValue:nil forKey:@"age"];
    
    //通过kvc取值
    NSLog(@"person的名字是%@",[p valueForKey:@"age"]);
    
    //打印结果
    //    不能将age设置为nil
    //    person的名字是0
    

    kvc处理Undefinekey异常

    通常kvc不允许调用一个不存在的key赋值,不然会报错forUndefinekey发生崩溃,所以重写forUndefinekey就可以避免崩溃
    eg:

    @interface Person : NSObject{
    
    }
    @end
    
    //创建对象Person
    Person *p = [[Person alloc] init];
    //通过kvc设置person 值
    [p setValue:nil forKey:@"age"];
    
    //通过kvc取值
    NSLog(@"person的名字是%@",[p valueForKey:@"age"]);
    
    //打印结果
    //    设置值时出现异常,该key不存在age
    //    出现异常,此key不存在age
    //    person的名字是(null)
    

    KVC使用

    KVC在iOS开发中是绝不可少的利器,这种基于运行时的编程方式极大地提高了灵活性,简化了代码,甚至实现很多难以想像的功能,KVC也是许多iOS开发黑魔法的基础。
    下面列举iOS开发中KVC的使用场景.
    1.动态地取值和设值
    利用KVC动态的取值和设值是最基本的用途了.
    2.用KVC来访问和修改私有变量
    对于类里的私有属性,Objective-C是无法直接访问的,但是KVC是可以的.
    3.Model和字典转换
    使用kvc和objc的runtime组合可以实现mode和字典的转换(YYModel)
    4.访问和修改这些控件的样式
    而KVC在大多数情况可下可以解决这个问题。最常用的就是个性化UITextField中的placeHolderText了.

    相关文章

      网友评论

          本文标题:KVC详解

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