KVC的全称是Key-Value Coding,俗称“键值编码”,可以通过一个key来访问某个属性
常见的API有
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (id)valueForKey:(NSString *)key;
setValue:forKey:的原理
//MJPerson
@interface MJPerson : NSObject
{
@public
int age;
int isAge;
int _isAge;
int _age;
}
@end
@implementation MJPerson
- (void)setAge:(int)age
{
NSLog(@"setAge: - %d", age);
}
- (void)_setAge:(int)age
{
NSLog(@"_setAge: - %d", age);
}
// 默认的返回值就是YES
+ (BOOL)accessInstanceVariablesDirectly
{
return YES;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
[person setValue:@10 forKey:@"age"];
}
return 0;
}
运行打印结果:

将 setAge: 方法注释掉,重新运行打印结果:

再将 _setAge: 方法注释掉,并将 accessInstanceVariablesDirectly 方法返回的结果设置为 NO,重新运行打印结果:

将 accessInstanceVariablesDirectly 方法返回的结果设置为 YES,或直接注释掉,运行

将 _age 注释掉,运行

将 _isAge 注释掉,运行

将 age 注释掉,运行

将 isAge 注释掉,运行

通过上面的操作,可得 setValue:forKey: 原理 如下

valueForKey:的原理
//MJPerson
@interface MJPerson : NSObject
{
@public
int age;
int isAge;
int _isAge;
int _age;
}
@end
@implementation MJPerson
- (int)getAge
{
return 14;
}
- (int)age
{
return 15;
}
- (int)isAge
{
return 16;
}
- (int)_age
{
return 17;
}
//默认的返回值就是YES
+ (BOOL)accessInstanceVariablesDirectly
{
return YES;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
person->_age = 10;
person->_isAge = 11;
person->age = 12;
person->isAge = 13;
NSLog(@"%@", [person valueForKey:@"age"]);
}
return 0;
}
运行打印结果为
14
将 getAge 方法注释掉,运行打印结果为
15
再将 age 方法注释掉,运行打印结果为
16
再将 isAge 方法注释掉,运行打印结果为
17
再将 _age 方法注释掉,并将 accessInstanceVariablesDirectly 方法返回的结果设置为 NO,运行打印结果为
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<MJPerson 0x1005074f0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key age.'
将 accessInstanceVariablesDirectly 方法返回的结果设置为 YES,或直接注释掉,运行打印结果为
10
将 _age 注释掉,运行打印结果为
11
将 _isAge 注释掉,运行打印结果为
12
将 age 注释掉,运行打印结果为
13
将 isAge 注释掉,运行打印结果为
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<MJPerson 0x10056f0e0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key age.'
通过上面的操作,可得 valueForKey:的原理如下

思考:通过 KVC 修改属性会触发 KVO 么?
//MJPerson
@interface MJPerson : NSObject
@property (assign, nonatomic) int age;
@end
@implementation MJPerson
@end
//MJObserver
@interface MJObserver : NSObject
@end
@implementation MJObserver
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"observeValueForKeyPath - %@", change);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJObserver *observer = [[MJObserver alloc] init];
MJPerson *person = [[MJPerson 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"];
}
return 0;
}
打印结果:

由打印结果可看出,通过 KVC 修改属性会触发 KVO。
网友评论