KVC的全称是Key-Value Coding,翻译成中文是 键值编码,键值编码是由NSKeyValueCoding非正式协议启用的一种机制,对象采用该协议来间接访问其属性。既可以通过一个字符串key来访问某个属性。这种间接访问机制补充了实例变量及其相关的访问器方法所提供的直接访问。
取值与设值
//通过setValue中key来设值
- (void)setValue:(nullable id)value forKey:(NSString *)key;
//通过valueForKey中key来取值
- (nullable id)valueForKey:(NSString *)key;
//通过valueForKeyPath中keyPath来设置值
- (nullable id)valueForKeyPath:(NSString *)keyPath;
//keyPath来取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
其他API
/默认返回YES,表示如果没有找到Set<Key>方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员,设置成NO就不这样搜索
+ (BOOL)accessInstanceVariablesDirectly;
//KVC提供属性值正确性验证的API,它可以用来检查set的值是否正确、为不正确的值做一个替换值或者拒绝设置新值并返回错误原因。
- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
//这是集合操作的API,里面还有一系列这样的API,如果属性是一个NSMutableArray,那么可以用这个方法来返回。
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
//如果Key不存在,且KVC无法搜索到任何和Key有关的字段或者属性,则会调用这个方法,默认是抛出异常。
- (nullable id)valueForUndefinedKey:(NSString *)key;
//和上一个方法一样,但这个方法是设值。
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
//如果你在SetValue方法时面给Value传nil,则会调用这个方法
- (void)setNilValueForKey:(NSString *)key;
//输入一组key,返回该组key对应的Value,再转成字典返回,用于将Model转到字典。
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
setValue:forKey: 设置值步骤
- 步骤一:
首先找这三种setter:方法,分别是:set<Key>->_set<Key>->setIs<Key>,其中<Key>是成员变量的名字,且首字母大写,比如本文中的name,顺序就是setName->_setName->setIsName。当找到这三种setter中任意一个时,则进行赋值,如果没有找到则进入步骤二. - 步骤二:
判断+ (BOOL)accessInstanceVariablesDirectly函数是否返回YES,如果返回YES,则按照 _key->_iskey->key->iskey的顺序搜索成员,找到任意一个则进行赋值,否则进入步骤三;如果返回NO,则直接进入步骤三。 -
步骤三:
如果setter方法 或者 实例变量都没有找到,系统会执行该对象的setValue:forUndefinedKey:函数,默认抛出异常,所以我们使用KVC进行解析模型数据时,要实现setValue:forUndefinedKey:函数,否则会崩溃。
image.png
KVC取值valueForKey:操作流程如下:
- 步骤一:
调用getter方法,调用顺序是:get<Key> -> <key> -> is<Key> -> _<key>,以name为例,getName -> name -> isName -> _name,如果找到,则直接返回对应的值(可能是对象),如果找不到则进入步骤二 - 步骤二:
判断+ (BOOL)accessInstanceVariablesDirectly函数是否返回YES,如果为YES,则访问成员变量,顺序为:_<key>->_is<Key>-><key>-> is<Key>,以name为例则是_name->_isName->name-> isName,找到则返回对应的值(可能是对象),找不到则进入步骤三;如果返回的是NO,则截止进入步骤三。 - 步骤三:
均找不到,则调用valueForUndefinedKey:抛出异常。
KVC 使用场景
-
1、动态设值和取值
常用的可以通过setValue:forKey: 和 valueForKey:
也可以通过路由的方式setValue:forKeyPath: 和 valueForKeyPath: -
2、通过KVC访问和修改私有变量
在日常开发中,对于类的私有属性,在外部定义的对象,是无法直接访问私有属性的,但是对于KVC而言,一个对象没有自己的隐私,所以可以通过KVC修改和访问任何私有属性 -
3、多值操作(model和字典互转)
model和字典的转换可以通过下面两个KVC的API实现
//字典转模型
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;
//模型转字典
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
-
4、修改一些系统空间的内部属性
在日常开发中,我们知道,很多UI控件都是在其内部由多个UI空间组合而成,这些内部控件苹果并没有提供访问的API,但是使用KVC可以解决这个问题,常用的就是自定义tabbar、个性化UITextField中的placeHolderText -
5、用KVC实现高阶消息传递
在对容器类使用KVC时,valueForKey:将会被传递给容器中的每一个对象,而不是对容器本身进行操作,结果会被添加到返回的容器中,这样,可以很方便的操作集合 来返回 另一个集合
//KVC实现高阶消息传递
- (void)transmitMsg{
NSArray *arrStr = @[@"english", @"franch", @"chinese"];
NSArray *arrCapStr = [arrStr valueForKey:@"capitalizedString"];//capitalizedString 字符串首字母大写
for (NSString *str in arrCapStr) {
NSLog(@"%@", str);
}
NSArray *arrCapStrLength = [arrCapStr valueForKeyPath:@"capitalizedString.length"];
for (NSNumber *length in arrCapStrLength) {
NSLog(@"%ld", (long)length.integerValue);
}
}
2021-04-02 15:56:06.908643+0800 IOS--多继承[7368:229757] English
2021-04-02 15:56:06.908789+0800 IOS--多继承[7368:229757] Franch
2021-04-02 15:56:06.908911+0800 IOS--多继承[7368:229757] Chinese
2021-04-02 15:56:06.909082+0800 IOS--多继承[7368:229757] 7
2021-04-02 15:56:06.909223+0800 IOS--多继承[7368:229757] 6
2021-04-02 15:56:06.909361+0800 IOS--多继承[7368:229757] 7
网友评论