KVC的查询顺序

一、自定义一个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:会直接崩溃。(大家都知道)
直接崩溃.png

1.KVC取值查找的第一个方法getName

#import "TestObject.h"

@interface TestObject()

@end

@implementation TestObject

- (NSString *)getName {
    
    return @"getName";
}

@end

当我们在TestObject类中实现了getName方法后,valueForKey:就不会再崩溃,而是调用getName方法,获取返回值。


控制台打印:

image.png

2.TestObject类中如果没有实现getName方法,KVC会查找第二个方法name,如果有getName方法,就不会继续往下查找了,因为getName方法已经生效了。

#import "TestObject.h"

@interface TestObject()

@end

@implementation TestObject

//- (NSString *)getName {
//
//    return @"getName";
//}

- (NSString *)name {
    
    return @"name";
}



控制台打印:

image.png

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";
}



控制台打印:

image.png

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";
}



控制台打印:

image.png

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;
}
控制台:
image.png
  • 当类中没有_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;
}
控制台:
image.png
  • 当类中没有_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;
}
控制台:
image.png
  • 当类中没有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;
}

控制台:
image.png

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



控制台打印:

image.png

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



控制台打印:

image.png

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
控制台:
image.png
  • 当类中没有_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
控制台:
image.png

后面两个成员变量我就不贴出来了,以此类推。

4.如果上述方法和成员变量都没找到,并且(BOOL)accessInstanceVariablesDirectly返回NO,KVC会走最后一步setValue:(id)value forUndefinedKey:(NSString *)key,若次方法依然没有找到,程序崩溃。


以上均为手打,如果错误敬请指正交流。

你可能感兴趣的:(KVC的查询顺序)