在这篇文章中,我们将探索KVC
的执行流程,首先我们可以从官方文档中找到该流程Apple Document KVC。
Basic Setter
- 1,首先查找
set<Key>:
或者_set<Key>
方法有没有实现。 - 2,如果实现了
accessInstanceVariablesDirectly
方法,并且返回值为YES
,则查找类似于_<key>, _is<Key>, <key>, or is<Key>
的变量名,如果找到一个,就将值赋给该属性。 - 3,如果没找到方法或者属性,就会唤起
[valueForUndefinedKey:]
方法,如果没有实现该方法,则会造成crash
。
下面我们新建LYPerson
类,并设置name
属性的值,并使用 setValue(:)ForKey(:)
进行赋值
LYPerson *person = [[LYPerson alloc] init];
[person setValue:@"LY" forKey:@"name"];
@interface LYPerson : NSObject {
@public
NSString *_isName;
NSString *name;
NSString *isName;
NSString *_name;
}
@end
@implementation LYPerson
//MARK: - setKey. 的流程分析
- (void)setName:(NSString *)name{
NSLog(@"%s - %@",__func__,name);
}
- (void)_setName:(NSString *)name{
NSLog(@"%s - %@",__func__,name);
}
- (void)setIsName:(NSString *)name{
NSLog(@"%s - %@",__func__,name);
}
- (void)_setIsName:(NSString *)name {
NSLog(@"%s - %@", __func__, name);
}
@end
LYPerson *person = [[LYPerson alloc] init];
[person setValue:@"LY" forKey:@"name"];
// 输出结果
2020-10-24 15:41:50.788791+0800 002-KVC取值&赋值过程[54677:6355080] -[LYPerson setName:] - LY
- 如果有
setName
方法,则会调用setName
方法,不会再调用其他方法。
下一步,我们将setName
方法去掉
//- (void)setName:(NSString *)name{
// NSLog(@"%s - %@",__func__,name);
//}
- (void)_setName:(NSString *)name{
NSLog(@"%s - %@",__func__,name);
}
- (void)setIsName:(NSString *)name{
NSLog(@"%s - %@",__func__,name);
}
- (void)_setIsName:(NSString *)name {
NSLog(@"%s - %@", __func__, name);
}
// 运行结果
2020-10-24 15:46:13.075257+0800 002-KVC取值&赋值过程[54767:6360026] -[LYPerson _setName:] - LY
- 如果没有实现
setName
方法,就会调用_setName
方法。
我们将 _setName
注释掉,看下运行结果:
//- (void)setName:(NSString *)name{
// NSLog(@"%s - %@",__func__,name);
//}
//- (void)_setName:(NSString *)name{
// NSLog(@"%s - %@",__func__,name);
//}
- (void)setIsName:(NSString *)name{
NSLog(@"%s - %@",__func__,name);
}
- (void)_setIsName:(NSString *)name {
NSLog(@"%s - %@", __func__, name);
}
2020-10-24 15:49:03.530758+0800 002-KVC取值&赋值过程[54820:6362797] -[LYPerson setIsName:] - LY
接下来,我们将setIsName
注释掉
//MARK: - setKey. 的流程分析
//- (void)setName:(NSString *)name{
// NSLog(@"%s - %@",__func__,name);
//}
//- (void)_setName:(NSString *)name{
// NSLog(@"%s - %@",__func__,name);
//}
//- (void)setIsName:(NSString *)name{
// NSLog(@"%s - %@",__func__,name);
//}
- (void)_setIsName:(NSString *)name {
NSLog(@"%s - %@", __func__, name);
}
// 运行结果
并不会执行 _setIsName 方法。
小结
- 我们对
name
属性进行KVC
赋值时,会依次查找setName -> _setName -> setIsName
方法。 - 如果实现了
setName
方法,则不会继续往下查找。
Basic Getter
使用KVC
进行取值时,使用valueForKey:
方法,它的调用流程如下:
- 1,首先会按照
get<Key>, <key>, is<Key>, or _<key>
的顺序依次进行查找。 - 2,如果该值为一个对象指针,则直接返回该值。
- 3,如果没有找到值,则会唤起
[valueForUndefinedKey:]
方法,如果没有实现这个方法,则会引起crash
。
我们沿用上面的例子,增加其set
方法
- (NSString *)getName{
return NSStringFromSelector(_cmd);
}
- (NSString *)name{
return NSStringFromSelector(_cmd);
}
- (NSString *)isName{
return NSStringFromSelector(_cmd);
}
- (NSString *)_name{
return NSStringFromSelector(_cmd);
}
我们对name
属性进行取值,并查看打印结果
NSLog(@"取值:%@",[person valueForKey:@"name"]);
2020-10-24 16:19:16.308404+0800 002-KVC取值&赋值过程[55401:6394378] 取值:getName
- 1,由于我们实现了
getName
方法,则 其他get方法
就不会被调用。
读者如果有兴趣的话,可以将getName
,name
,isName
,_name
方法注释掉,查看其调用情况。
网友评论