定义
KVC 是 Key Value Coding 的简称,键值对编码,遵循 NSKeyValueCoding
协议,可以像操作字典一样操作一个对象,通过 key 来直接取值和赋值的机制,而不是通过调用 setter、getter 方法访问。
相关API
由下图 NSKeyValueCoding.h
头文件中,我们可以看到一些相关的 API。
其中,以下四个是我们较为常用的 API
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (id)valueForKey:(NSString *)key;
API 区别
这里简单说一下 forKey
以及 forKeyPath
的区别。
@interface Student : NSObject
@property (nonatomic, assign) int age;
@end
@interface Teacher : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) Student *student;
@end
如上述代码所示,一个 Teacher
类中,包含一个 name
的属性和一个 student
的对象,Student
对象中包含 age
的属性,那么,如下代码所示可以看出,对于 Teacher
类中的 name
属性使用 KVC 方法,无论是使用 setValue: forKey:
或是 setValue: forKeyPath:
都是可以实现的。但是,如果是对 student
中的 age
,则必须使用 setValue: forKeyPath:
方法。
所以,我们不难看出,key
是只能访问当前对象的属性,如果想要层层向下访问的话,就需要使用 keyPath
。
self.teacher = [[Teacher alloc] init];
self.teacher.name = @"老明";
// 这里如果想要对 student 中的属性进行赋值,那么必须先对其进行实例化
self.teacher.student = [[Student alloc] init];
self.teacher.student.age = 10;
[self.teacher setValue:@"老李" forKey:@"name"];
NSLog(@"teacherName1 = %@",self.teacher.name);
NSLog(@"studentAge1 = %d",self.teacher.student.age);
[self.teacher setValue:@"老刘" forKeyPath:@"name"];
[self.teacher setValue:@30 forKeyPath:@"student.age"];
NSLog(@"teacherName2 = %@",self.teacher.name);
NSLog(@"studentAge2 = %d",self.teacher.student.age);
log打印出来结果如下:
teacherName1 = 老李
studentAge1 = 10
teacherName2 = 老刘
studentAge2 = 30
是否触发 KVO
对 teacher 的 name 属性执行监听,查看其回调方法observeValueForKeyPath:ofObject:change:context:
是否执行
// 1. 添加 KVO 监听
[self.teacher addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
// 2. 通过 KVC 修改 name 属性值
[self.teacher setValue:@"teacherNew" forKey:@"name"];
// 3. 移除监听
[self.teacher removeObserver:self forKeyPath:@"name"];
log 打印结果如下:
object: <Teacher: 0x600001f1ddc0>
keyPath: name
change: {
kind = 1;
new = teacherNew;
old = "\U8001\U5218";
}
从上述打印结果不难看出,KVC 修改属性会触发 KVO。
setValue:forKey: 原理
- setValue:forKey: 方法在调用时,首先会去调用
setKey:
方法,如果找不到方法,则会查找调用_setKey:
的方法,如果找到方法,那么直接传递参数调用方法,如果两个方法均找不到,那么调用accessInstanceVariablesDirectly
。
其中 accessInstanceVariablesDirectly(是否能直接访问成员变量) 方法的默认返回值是YES。 - 若
accessInstanceVariablesDirectly
返回 NO,则抛出异常。 - 若
accessInstanceVariablesDirectly
返回 YES,则按顺序_key、_isKey、key、isKey
依次往后的顺序去查找成员变量,如果找到成员变量,则直接赋值,找不到则抛出异常。
具体方法调用步骤,可参照下图所示流程:
如果所示步骤,可通过依次代码设置
setKey
以及 _setKey:
方法来进行验证。
valueForKey: 原理
通过 setValue:forKey:
方法,不难得出 valueForKey:
的执行顺序。
- 按顺序
getKey、key、 isKey、_key
依次往后的顺序去调用取值,如果找到方法,则直接调用方法。 - 若
accessInstanceVariablesDirectly
返回 NO,则抛出异常。 - 若
accessInstanceVariablesDirectly
返回 YES,则按顺序_key、_isKey、key、isKey
依次往后的顺序去查找成员变量,如果找到成员变量,则直接取值,找不到则抛出异常。
valueForKey:
网友评论