1、KVC<
Key-value-coding>
键值编码,单看这个名字可能不太好理解。其实翻译一下就很简单了,就是指iOS的开发中,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存取方法。这样就可以在运行时动态地访问和修改对象的属性。而不是在编译时确定,这也是iOS开发中的黑魔法之一,很多高级的iOS开发技巧都是基于KVC实现的。无论是Swift
还是Objective-C
,KVC的定义都是对NSObject
的扩展来实现的(Objective-C
中有个显式的NSKeyValueCoding
类别名,而Swift没有,也不需要)。所以对于所有继承了NSObject
的类型,也就是几乎所有的Objective-C
对象都能使用KVC
(一些纯Swift
类和结构体是不支持KVC的),下面是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转到字典。
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;
//用于字典转模型
2、KVC
底层原理
setValue:forKey:
1.程序优先调用
set<Key>:属性值
方法,代码通过setter
方法完成设置。注意,这里的<key>是指成员变量名,首字母大小写要符合KVC的命名规则,下同2.如果没有找到
setName:
方法,KVC机制会检查+ (BOOL)accessInstanceVariablesDirectly
方法有没有返回YES,默认该方法会返回YES,如果你重写了该方法让其返回NO的话,那么在这一 步KVC会执行setValue:forUndefinedKey:
方法,不过一般开发者不会这么做。3.如果返回
YES
按照_key、_isKey、key、isKey
的顺序查找成员变量,找到了就直接赋值。4.如果上面列出的方法或者成员变量都不存在,系统将会执行该对象的
setValue:forUndefinedKey:
方法,默认是抛出异常。
valueForKey:
1.在取值过程中按顺序看getKey、key、isKey、_getKey、_key五个方法是否实现,若实现了则取到的值为方法返回的值,所以本质上是按先后顺序调用了这五个getter方法,如果没有,则会询问+ (BOOL)accessInstanceVariablesDirectly方法能否直接取成员变量,若返回YES,则会按顺序取_key、_isKey、 key、isKey的值。
2.如果在+ (BOOL)accessInstanceVariablesDirectly中返回NO,或者取到isKey仍然取不到值,则会调用- (nullable id)valueForUndefinedKey方法,该方法中返回的值即为取到的值。
KVC取值的补充:
在取值的第一步结束第二步开始之前,还会判断获取的值是否是数组或者集合。
如果上面的
getter
没有找到,KVC则会查找countOf<Key>
,objectIn<Key>AtIndex
或<Key>AtIndexes
格式的方法。如果countOf<Key>
方法和另外两个方法中的一个被找到,那么就会返回一个可以响应NSArray
所有方法的代理集合(它是NSKeyValueArray
,是NSArray
的子类),调用这个代理集合的方法,或者说给这个代理集合发送属于NSArray
的方法,就会以countOf<Key>
,objectIn<Key>AtIndex
或<Key>AtIndexes
这几个方法组合的形式调用。还有一个可选的get<Key>:range:
方法。所以你想重新定义KVC的一些功能,你可以添加这些方法,需要注意的是你的方法名要符合KVC的标准命名方法,包括方法签名。如果上面的方法没有找到,那么会同时查找
countOf<Key>
,enumeratorOf<Key>
,memberOf<Key>
格式的方法。如果这三个方法都找到,那么就返回一个可以响应NSSet
所的方法的代理集合,和上面一样,给这个代理集合发NSSet
的消息,就会以countOf<Key>
,enumeratorOf<Key>
,memberOf<Key>
组合的形式调用。
3、KVC的使用
动态地取值和设值
利用KVC动态的取值和设值是最基本的用途了。相信每一个iOS开发者都能熟练掌握,
用KVC来访问和修改私有变量
对于类里的私有属性,Objective-C是无法直接访问的,但是KVC是可以的
Model和字典转换
这是KVC强大作用的又一次体现,里面充分地运用了KVC和Objc的
runtime
组合的技巧,只用了短短数行代码就是完成了很多功能。修改一些控件的内部属性
最常用的就是个性化UITextField中的placeHolderText了。下面演示如果修改placeHolder的文字样式。这里的关键点是如果获取你要修改的样式的属性名,也就是key或者keyPath名。