一、前言
KVC(Key-value coding)键值编码,是对NSObjcet的扩展,分类名为 : NSKeyValueCoding
二、常用的方法说明
// 1、将键字符串key所对应的属性的值设置为value。不能设定属性值时,将会引起接收器调用方法2
- (void)setValue:(nullable id)value forKey:(NSString *)key
// 2、当属性值设置失败,调用此方法
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key
// 3、返回标识属性的键字符串所对应的值。如果获取失败,将会引起接收器调用方法4
- (nullable id)valueForKey:(NSString *)key
// 4、取值失败,调用此方法
- (nullable id)valueForUndefinedKey:(NSString *)key
// 5、在键字符串key所对应的"标量"型属性值设为nil,调用此方法,并抛出NSInvalidArgumentException异常(可demo测试)
- (void)setNilValueForKey:(NSString *)key
// 6、默认返回值YES,代表如果没有找到Set方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员,设置成NO就不这样搜索
+ (BOOL)accessInstanceVariablesDirectly
标量 : 属性中的单纯的数值(整数、实数、布尔值等)
在赋值的时候
如果是结构体,必须包装成NSValue实例
如果是标量型属性,必须包装成NSNumber实例
二、setValue:forKey:底层原理
原理如下:
- 先找相关方法
set<Key>:
,_set<Key>:
,setIs<Key>:
,如果有,优先调用setter方法完成赋值(注意:set后面的键的第一字字母必须是大写!!) - 如没有找到相关方法
+ (BOOL)accessInstanceVariablesDirectly
,判断是否可以直接方法成员变量 - 如果判断是
NO
,直接执行KVC的setValue:forUndefinedKey:
(系统抛出异常,未定义key) - 如果判断是
YES
,继续找相关的_<key>
,_is<Key>
,<key>
,is<Key>
- 方法或成员 都不存在,
setValue:forUndefinedKey:
方法,默认抛出异常
三、valueForKey:底层原理
原理如下:
- 先找相关方法
get<Key>:
,Key:
,countOfKey
,objectInKeyAtIndex
- 如没有找到相关方法
+ (BOOL)accessInstanceVariablesDirectly
,判断是否可以直接方法成员变量 - 如果上面判断为
NO
直接执行KVC的setValue:forUndefinedKey:
(系统抛出异常,未定义key) - 如果判断是
YES
继续找相关的_<key>
,_is<Key>
,<key>
,is<Key>
- 方法或成员 都不存在,
setValue:forUndefinedKey:
方法,默认抛出异常
四、KVC 为什么能能够触发 KVO
KVC 只所以能够触发 KVO,那是因为 在 KVC 底层有手动触发 KVO的代码,在前面的例子添加方法 willChangeValueForKey
和 didChangeValueForKey
可得到验证。
[person willChangeValueForKey:@"age"];
person->_age = 2;
[person didChangeValueForKey:@"age"];
五、使用场景
1、修改系统控件的样式
UITextField没有提供修改placeholder样式的API,但KVC可以访问类的私有属性,我们可以借助这一特性来修改其样式,但是目前版本已经被禁用了
[self.textField setValue:UIColor.redColor
forKeyPath:@"_placeholderLabel.textColor"];
2、字典模型互转
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
@implementation Person
- (NSString *)description {
return [NSString stringWithFormat:@"name:%@, age:%zd", _name, _age];
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
NSDictionary *dict = @{@"name" : @"zhangSan",
@"age" : @(1)};
Person *person = [Person new];
[person setValuesForKeysWithDictionary:dict];
NSLog(@"字典转模型---%@", person);
NSDictionary *newDict = [person dictionaryWithValuesForKeys:@[@"name", @"age"]];
NSLog(@"模型转字典---%@", newDict);
}
// 打印
字典转模型---name:zhangSan, age:1
模型转字典---{
age = 1;
name = zhangSan;
}
3、操作集合
NSArray *array = @[@"china", @"english", @"american"];
NSArray *capArray = [array valueForKey:@"capitalizedString"];
NSLog(@"capitalizedString---%@", capArray);
NSArray *lengthArray = [array valueForKeyPath:@"capitalizedString.length"];
NSLog(@"capitalizedString.length---%@", lengthArray);
// 打印
capitalizedString---(
China,
English,
American
)
capitalizedString.length---(
5,
7,
8
)