前言:
KVC的全称是Key-Value Coding
,翻译成中文是键值编码
,键值编码是由NSKeyValueCoding非正式协议启用的一种机制,对象采用该协议来间接访问其属性。既可以通过一个字符串key来访问某个属性。这种间接访问机制补充了实例变量及其相关的访问器方法所提供的直接访问。
原理探究:KVC取值
测试代码: 探索 set 方法步骤
#import "JTestVC.h"
@interface JPerson : NSObject
@end
@implementation JPerson
@end
@interface JTestVC ()
@end
@implementation JTestVC
- (void)viewDidLoad {
[super viewDidLoad];
JPerson *person = [JPerson new];
[person setValue:@"张三" forKey:@"name"];
}
运行:**** 崩溃 *******
修改代码1:
@interface JPerson : NSObject
@end
@implementation JPerson
- (void)setName:(NSString*)name{
NSLog(@"__%s__",__func__);
}
@end
运行:成功
修改代码2:
@interface JPerson : NSObject
@end
@implementation JPerson
//- (void)setName:(NSString*)name{
// NSLog(@"__%s__",__func__);
//}
- (void)_setName:(NSString*)name{
NSLog(@"__%s__",__func__);
}
@end
运行:成功
修改代码3:
@interface JPerson : NSObject
@end
@implementation JPerson
//- (void)setName:(NSString*)name{
// NSLog(@"__%s__",__func__);
//}
//- (void)_setName:(NSString*)name{
// NSLog(@"__%s__",__func__);
//}
- (void)setIsName:(NSString*)name{
NSLog(@"__%s__",__func__);
}
@end
运行:成功
修改代码4:当注释都放开的时候
@interface JPerson : NSObject
@end
@implementation JPerson
- (void)setName:(NSString*)name{ //
NSLog(@"__%s__",__func__);
}
- (void)_setName:(NSString*)name{
NSLog(@"__%s__",__func__);
}
- (void)setIsName:(NSString*)name{
NSLog(@"__%s__",__func__);
}
@end
运行:只进入 setName 方法
修改代码5:
@interface JPerson : NSObject
@end
@implementation JPerson
//- (void)setName:(NSString*)name{
// NSLog(@"__%s__",__func__);
//}
- (void)_setName:(NSString*)name{
NSLog(@"__%s__",__func__);
}
- (void)setIsName:(NSString*)name{
NSLog(@"__%s__",__func__);
}
运行:只进入 _setName 方法
得出结论:
setValue
过程中会查找是否有这三种setter
方法,按照查找顺序为set<Key>
:-> _set<Key>
-> setIs<Key>
测试代码: 探索 成员变量
@interface JPerson : NSObject
@end
@implementation JPerson
{
NSString* name;
NSString* _name;
NSString* _isName;
NSString* isName;
}
- (void)testLog{
NSLog(@"name = %@,_name = %@,isName = %@,_isName = %@",name,_name,_isName,isName);
}
@end
@interface JTestVC ()
@end
@implementation JTestVC
- (void)viewDidLoad {
[super viewDidLoad];
JPerson *person = [JPerson new];
[person setValue:@"张三" forKey:@"name"];
[person testLog];
}
运行输出:
2021-01-06 14:38:41.541081+0800 TestDemo[6532:135946]
name = (null)
_name = 张三
isName = (null)
_isName = (null)
修改代码:去掉成员变量 _name
@implementation JPerson
{
NSString* name;
NSString* _isName;
NSString* isName;
}
运行输出:
2021-01-06 14:40:15.753638+0800 TestDemo[6628:137657]
name = (null)
isName = 张三
_isName = (null)
修改代码:去掉成员变量 _isName
@implementation JPerson
{
NSString* name;
NSString* isName;
}
- (void)testLog{
NSLog(@"\nname = %@\nisName = %@\n",name,isName);
}
@end
运行输出:
2021-01-06 14:41:41.229603+0800 TestDemo[6775:139723]
name = 张三
isName = (null)
得出结论:
查找间接访问的实例变量进行赋值,查找顺序为:_<key>
->_is<Key>
-> <key>
->is<Key>
修改代码: 加上 accessInstanceVariablesDirectly 方法,并且返回NO
@interface JPerson : NSObject
@end
@implementation JPerson
{
NSString* name;
NSString* isName;
NSString* _name;
NSString* _isName;
}
+ (BOOL)accessInstanceVariablesDirectly{
return NO;
}
- (void)testLog{
NSLog(@"\nname = %@\nisName = %@\n",name,isName);
}
@end
运行:崩溃
得出结论:
即使有成员变量,当 accessInstanceVariablesDirectly
返回为NO
的时候,也会造成崩溃 ,即 accessInstanceVariablesDirectly
方法阻断了查找成员变量的过程。
修改代码: 加上 setValue:(id)value forUndefinedKey:(NSString *)key
@interface JPerson : NSObject
@end
@implementation JPerson
{
NSString* name;
NSString* isName;
NSString* _name;
NSString* _isName;
}
+ (BOOL)accessInstanceVariablesDirectly{
return NO;
}
- (void)testLog{
NSLog(@"\nname = %@\nisName = %@\n",name,isName);
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"%@ is not founded",key);
}
@end
运行:正常,并且成功进入此 forUndefinedKey 方法
2021-01-06 14:50:18.511538+0800 TestDemo[7023:145721] name is not founded
得出结论:
如果以上步骤均不成立的话,那么最后就会进入setValue:forUndefinedKey
方法