一、自定义一个TestObject类,测试KVC取值的查询顺序
以下是TestObject
的源码,你没看错,就是这样一个没有实现任何方法,没有任何属性的类。我们用TestObject
类来探究KVC取值的查询顺序。
.h
#import
@interface TestObject : NSObject
@end
.m
#import "TestObject.h"
@interface TestObject()
@end
@implementation TestObject
@end
KVC不论取值还是赋值都会先去查询相应的方法,如果一个方法都没找到,再按规则去找成员变量。
我们在代码中调用KVC的取值:
TestObject *obj = [TestObject new];
NSString *name = [obj valueForKey:@"name"];
NSLog(@"%@",name);
在未声明属性,未实现任何方法,类中无对应成员变量时使用valueForKey:
会直接崩溃。(大家都知道)
1.KVC取值查找的第一个方法getName
#import "TestObject.h"
@interface TestObject()
@end
@implementation TestObject
- (NSString *)getName {
return @"getName";
}
@end
当我们在TestObject
类中实现了getName
方法后,valueForKey:
就不会再崩溃,而是调用getName
方法,获取返回值。
控制台打印:
2.TestObject
类中如果没有实现getName
方法,KVC会查找第二个方法name
,如果有getName
方法,就不会继续往下查找了,因为getName
方法已经生效了。
#import "TestObject.h"
@interface TestObject()
@end
@implementation TestObject
//- (NSString *)getName {
//
// return @"getName";
//}
- (NSString *)name {
return @"name";
}
控制台打印:
3.TestObject
类中如果没有实现getName
方法,也没有实现name
方法,KVC会查找第三个方法isName
。
#import "TestObject.h"
@interface TestObject()
@end
@implementation TestObject
//- (NSString *)getName {
//
// return @"getName";
//}
//
//- (NSString *)name {
//
// return @"name";
//}
- (NSString *)isName {
return @"isName";
}
控制台打印:
4.若前三个方法都没有实现,KVC会查找第四个方法_name
。
#import "TestObject.h"
@interface TestObject()
@end
@implementation TestObject
//- (NSString *)getName {
//
// return @"getName";
//}
//
//- (NSString *)name {
//
// return @"name";
//}
//- (NSString *)isName {
//
// return @"isName";
//}
- (NSString *)_name {
return @"_name";
}
控制台打印:
5.如果上述方法都没实现,接下来KVC会将要取的值看做数组,调用下面的方法:
- (NSUInteger)countOfName
(必须实现)
以下两个二选一
- (id)objectInNameAtIndex:(NSUInteger)index
(优先查找)
- (id)nameAtIndexes:(id)indexes
(其次查找)
6.以上方法都没有找到,那么KVC会按照集合(NSSet
)来处理,调用下面三个方法:
- (NSUInteger)countOfName
(必须实现)
- (id)enumeratorOfName
(必须实现)
- (id)memberOfName:(id)name
(必须实现)
7.方法查询到此为止,如果上述方法都没有查找到,接下来会按照顺序查找成员变量:
_name
;
_isName
;
name
;
isName
;
-
第一次调用:
源码:
#import "TestObject.h"
@interface TestObject(){
NSString *_name;
NSString *_isName;
NSString *name;
NSString *isName;
}
@end
@implementation TestObject
- (instancetype)init
{
self = [super init];
if (self) {
_name = @"ivar : _name";
_isName = @"ivar : _isName";
name = @"ivar : name";
isName = @"ivar : isName";
}
return self;
}
控制台:
-
当类中没有
_name
成员变量时,我们进行第二次调用:
源码:
#import "TestObject.h"
@interface TestObject(){
// NSString *_name;
NSString *_isName;
NSString *name;
NSString *isName;
}
@end
@implementation TestObject
- (instancetype)init
{
self = [super init];
if (self) {
// _name = @"ivar : _name";
_isName = @"ivar : _isName";
name = @"ivar : name";
isName = @"ivar : isName";
}
return self;
}
控制台:
-
当类中没有
_isName
成员变量时,我们进行第三次调用:
源码:
#import "TestObject.h"
@interface TestObject(){
// NSString *_name;
// NSString *_isName;
NSString *name;
NSString *isName;
}
@end
@implementation TestObject
- (instancetype)init
{
self = [super init];
if (self) {
// _name = @"ivar : _name";
// _isName = @"ivar : _isName";
name = @"ivar : name";
isName = @"ivar : isName";
}
return self;
}
控制台:
-
当类中没有
name
成员变量时,我们进行第四次调用:
源码:
#import "TestObject.h"
@interface TestObject(){
// NSString *_name;
// NSString *_isName;
// NSString *name;
NSString *isName;
}
@end
@implementation TestObject
- (instancetype)init
{
self = [super init];
if (self) {
// _name = @"ivar : _name";
// _isName = @"ivar : _isName";
// name = @"ivar : name";
isName = @"ivar : isName";
}
return self;
}
控制台:
8.如果上述方法和成员变量都没找到,KVC会走最后一步(id)valueForUndefinedKey:(NSString *)key
,若次方法依然没有找到,程序崩溃。
二、定义一个TestObject2
类,测试KVC赋值的查询顺序
以下是TestObject2
的源码:
.h
#import
@interface TestObject2 : NSObject
@end
.m
#import "TestObject2.h"
@interface TestObject2()
@end
@implementation TestObject2
@end
我们在代码中调用KVC的赋值:
TestObject2 *obj = [TestObject2 new];
[obj setValue:@"TestString" forKey:@"name"];
在未声明属性,未实现任何方法,类中无对应成员变量时使用setValue: forKey:
会直接崩溃。(这个大家也知道)
1.KVC赋值查找的第一个方法setName:
#import "TestObject2.h"
@interface TestObject2()
@end
@implementation TestObject2
- (void)setName:(NSString *)name {
NSLog(@"%s\t%@",__func__,name);
}
@end
控制台打印:
2.如果setName:
没有找到,KVC会查找第二个方法_setName:
#import "TestObject2.h"
@interface TestObject2()
@end
@implementation TestObject2
//- (void)setName:(NSString *)name {
// NSLog(@"%s\t%@",__func__,name);
//}
- (void)_setName:(NSString *)name {
NSLog(@"%s\t%@",__func__,name);
}
@end
控制台打印:
3.如果前面两个方法都没有找到,接下来会调用(BOOL)accessInstanceVariablesDirectly
,如果返回NO则不去查找成员变量,如果返回YES则接下来按照下列顺序规则查找成员变量。
_name
;
_isName
;
name
;
isName
;
-
第一次调用:
源码:
#import "TestObject2.h"
@interface TestObject2(){
NSString *_name;
NSString *_isName;
NSString *name;
NSString *isName;
}
@end
@implementation TestObject2
+ (BOOL)accessInstanceVariablesDirectly {
return YES;
}
- (void)printName {
NSLog(@"_name : %@",_name);
NSLog(@"_isName : %@",_isName);
NSLog(@"name : %@",name);
NSLog(@"isName : %@",isName);
}
@end
控制台:
-
当类中没有
_name
成员变量时,我们进行第二次调用:
源码:
#import "TestObject2.h"
@interface TestObject2(){
// NSString *_name;
NSString *_isName;
NSString *name;
NSString *isName;
}
@end
@implementation TestObject2
+ (BOOL)accessInstanceVariablesDirectly {
return YES;
}
- (void)printName {
// NSLog(@"_name : %@",_name);
NSLog(@"_isName : %@",_isName);
NSLog(@"name : %@",name);
NSLog(@"isName : %@",isName);
}
@end
控制台:
后面两个成员变量我就不贴出来了,以此类推。