一、自定義一個(gè)TestObject類,測(cè)試KVC取值的查詢順序
以下是TestObject的源碼,你沒看錯(cuò),就是這樣一個(gè)沒有實(shí)現(xiàn)任何方法,沒有任何屬性的類。我們用TestObject類來探究KVC取值的查詢順序。
.h
#import <Foundation/Foundation.h>
@interface TestObject : NSObject
@end
.m
#import "TestObject.h"
@interface TestObject()
@end
@implementation TestObject
@end
KVC不論取值還是賦值都會(huì)先去查詢相應(yīng)的方法,如果一個(gè)方法都沒找到,再按規(guī)則去找成員變量。
我們?cè)诖a中調(diào)用KVC的取值:
TestObject *obj = [TestObject new];
NSString *name = [obj valueForKey:@"name"];
NSLog(@"%@",name);
在未聲明屬性,未實(shí)現(xiàn)任何方法,類中無對(duì)應(yīng)成員變量時(shí)使用valueForKey:會(huì)直接崩潰。??(大家都知道)

1.KVC取值查找的第一個(gè)方法getName
#import "TestObject.h"
@interface TestObject()
@end
@implementation TestObject
- (NSString *)getName {
return @"getName";
}
@end
當(dāng)我們?cè)?code>TestObject類中實(shí)現(xiàn)了getName方法后,valueForKey:就不會(huì)再崩潰,而是調(diào)用getName方法,獲取返回值。
控制臺(tái)打?。?br>

2.TestObject類中如果沒有實(shí)現(xiàn)getName方法,KVC會(huì)查找第二個(gè)方法name,如果有getName方法,就不會(huì)繼續(xù)往下查找了,因?yàn)?code>getName方法已經(jīng)生效了。
#import "TestObject.h"
@interface TestObject()
@end
@implementation TestObject
//- (NSString *)getName {
//
// return @"getName";
//}
- (NSString *)name {
return @"name";
}
控制臺(tái)打?。?br>

3.TestObject類中如果沒有實(shí)現(xiàn)getName方法,也沒有實(shí)現(xiàn)name方法,KVC會(huì)查找第三個(gè)方法isName。
#import "TestObject.h"
@interface TestObject()
@end
@implementation TestObject
//- (NSString *)getName {
//
// return @"getName";
//}
//
//- (NSString *)name {
//
// return @"name";
//}
- (NSString *)isName {
return @"isName";
}
控制臺(tái)打印:

4.若前三個(gè)方法都沒有實(shí)現(xiàn),KVC會(huì)查找第四個(gè)方法_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";
}
控制臺(tái)打?。?br>

5.如果上述方法都沒實(shí)現(xiàn),接下來KVC會(huì)將要取的值看做數(shù)組,調(diào)用下面的方法:
- (NSUInteger)countOfName(必須實(shí)現(xiàn))
以下兩個(gè)二選一
- (id)objectInNameAtIndex:(NSUInteger)index(優(yōu)先查找)
- (id)nameAtIndexes:(id)indexes(其次查找)
6.以上方法都沒有找到,那么KVC會(huì)按照集合(NSSet)來處理,調(diào)用下面三個(gè)方法:
- (NSUInteger)countOfName(必須實(shí)現(xiàn))
- (id)enumeratorOfName(必須實(shí)現(xiàn))
- (id)memberOfName:(id)name(必須實(shí)現(xiàn))
7.方法查詢到此為止,如果上述方法都沒有查找到,接下來會(huì)按照順序查找成員變量:
_name;
_isName;
name;
isName;
-
第一次調(diào)用:
源碼:
#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;
}
控制臺(tái):

-
當(dāng)類中沒有
_name成員變量時(shí),我們進(jìn)行第二次調(diào)用:
源碼:
#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;
}
控制臺(tái):

-
當(dāng)類中沒有
_isName成員變量時(shí),我們進(jìn)行第三次調(diào)用:
源碼:
#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;
}
控制臺(tái):

-
當(dāng)類中沒有
name成員變量時(shí),我們進(jìn)行第四次調(diào)用:
源碼:
#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;
}
控制臺(tái):

8.如果上述方法和成員變量都沒找到,KVC會(huì)走最后一步(id)valueForUndefinedKey:(NSString *)key,若次方法依然沒有找到,程序崩潰。
二、定義一個(gè)TestObject2類,測(cè)試KVC賦值的查詢順序
以下是TestObject2的源碼:
.h
#import <Foundation/Foundation.h>
@interface TestObject2 : NSObject
@end
.m
#import "TestObject2.h"
@interface TestObject2()
@end
@implementation TestObject2
@end
我們?cè)诖a中調(diào)用KVC的賦值:
TestObject2 *obj = [TestObject2 new];
[obj setValue:@"TestString" forKey:@"name"];
在未聲明屬性,未實(shí)現(xiàn)任何方法,類中無對(duì)應(yīng)成員變量時(shí)使用setValue: forKey:會(huì)直接崩潰。??(這個(gè)大家也知道)
1.KVC賦值查找的第一個(gè)方法setName:
#import "TestObject2.h"
@interface TestObject2()
@end
@implementation TestObject2
- (void)setName:(NSString *)name {
NSLog(@"%s\t%@",__func__,name);
}
@end
控制臺(tái)打?。?br>

2.如果setName:沒有找到,KVC會(huì)查找第二個(gè)方法_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
控制臺(tái)打印:

3.如果前面兩個(gè)方法都沒有找到,接下來會(huì)調(diào)用(BOOL)accessInstanceVariablesDirectly,如果返回NO則不去查找成員變量,如果返回YES則接下來按照下列順序規(guī)則查找成員變量。
_name;
_isName;
name;
isName;
-
第一次調(diào)用:
源碼:
#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
控制臺(tái):

-
當(dāng)類中沒有
_name成員變量時(shí),我們進(jìn)行第二次調(diào)用:
源碼:
#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
控制臺(tái):

后面兩個(gè)成員變量我就不貼出來了,以此類推。