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
属性赋值。
- 先去
Person
类里面找有没有setAge:
有的话传递参数,调用改该方法,否则进行第2步 - 找到
_setAge:
方法,有则传递参数,无进行第3步 - 找到
accessInstanceVariablesDirectly
这个方法,返回一个Bool值
- 返回为
NO
表示不允许访问成员变量,调用setValue:forUndefinedKey:
并抛出异常NSUnknownKeyException
- 返回为
YES
则可以访问,进行第4步
- 会按照
_key、_isKey、key、isKey
的顺序进行赋值,有则直接赋值,无则进行第5步 - 依然会抛出调用
setValue:forUndefinedKey:
并抛出异常NSUnknownKeyException
这个信息
2.2 valueForKey:的原理
原理- 按照
getKey、key、iskey、_key
的先后顺序查找方法,如果找到方法直接取值,否则进入第2步 - 找到
accessInstanceVariablesDirectly
这个方法,返回一个Bool值
- 返回为
NO
表示不允许访问成员变量,调用valueForUndefinedKey:
并抛出异常NSUnknownKeyException
- 返回为
YES
按照顺序查找成员变量_key、_isKey、key、isKey
,如果找到直接取值,否则进入第3步。
- 依然会抛出调用
valueForUndefinedKey:
并抛出异常NSUnknownKeyException
面试
- 通过KVC修改属性会触发KVO吗?
会。
我们自定义一个ObserverMyObserver
#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的过程
网友评论