OC语法:KVC的底层实现

2019-10-02  本文已影响0人  意一ineyee
一、KVC是什么
二、怎么使用KVC
三、KVC的底层实现
四、KVC常见面试题

一、KVC是什么


KVC全称Key-Value Coding,翻译过来是键值编码,是一种通过一个key来访问某个属性的机制。

解释一下,我们通常不都是通过settergetter方法来访问公开属性的嘛,私有属性更是访问不到,而KVC则是提供了另外一种访问属性的方式——即通过一个key来访问某个属性,无论是公开属性还是私有属性。

二、怎么使用KVC


KVC最常用的API如下,更多的API可以自己去查。

- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKey:(NSString *)key;

- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (id)valueForKeyPath:(NSString *)keyPath;

setValue:forKey:valueForKey:只能用来访问当前对象的属性,而setValue:forKeyPath:valueForKeyPath:还可以用来访问当前对象的属性的属性,并可以这样一层一层套下去,也就是说KeyPathKey这种方式要强大一些。

举个简单例子:

// INEPerson.h
#import <Foundation/Foundation.h>

// Cat类
@interface INECat : NSObject

@property (nonatomic, assign) NSInteger weight;

@end

@interface INEPerson : NSObject

// 公开属性
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong) INECat *cat;

@end


// INEPerson.m
@interface INEPerson ()

// 私有属性
@property (nonatomic, assign) NSInteger height;

@end
// ViewController.m
- (void)viewDidLoad {
    [super viewDidLoad];

    INEPerson *person = [[INEPerson alloc] init];
    
    // 通常是通过setter、getter方法来访问公开属性的
    person.age = 25;
    NSLog(@"%ld", person.age);// 25
    
    // 无法访问私有属性
//    person.height = 177;
//    NSLog(@"%ld", person.age);
    
    // 通过KVC来访问公开属性
    [person setValue:@26 forKey:@"age"];
    NSLog(@"%ld", [[person valueForKey:@"age"] integerValue]);// 26
    
    person.cat = [[INECat alloc] init];
    [person setValue:@11 forKeyPath:@"cat.weight"];
    NSLog(@"%ld", [[person valueForKeyPath:@"cat.weight"] integerValue]);// 11
    
    // 通过KVC来访问私有属性
    [person setValue:@177 forKey:@"height"];
    NSLog(@"%ld", [[person valueForKey:@"height"] integerValue]);// 177
}

三、KVC的底层实现


KeyPathKey是同理的,下面以Key方式为例。

1、KVC的赋值流程

setValue:forKey:方法内部:

首先会把参数key的第一个字母变成大写的。

然后根据这个Key按顺序去查找对应的setter方法,包括setKey:_setKey:,如果找到了对应的setter方法,就调用并把参数value传进去,从而修改掉属性的值(本质上还是修改掉了成员变量的值)。

如果没有找到对应的setter方法,就会去查看accessInstanceVariablesDirectly方法的返回值(能否直接访问成员变量,默认返回YES),如果不能直接访问成员变量,就会报一个“找不到key”的错。

如果能直接访问成员变量,就会按顺序去查找对应的成员变量,_key_isKeykeyisKey,如果找到了对应的成员变量,就直接给成员变量赋值(若设置了KVO的话,此时还会触发KVO,因为KVC在此处也调用了willChangeValueForKey:didChangeValueForKey:方法),如果没有找到对应的成员变量,就会报一个“找不到key”的错。

2、KVC的取值流程

valueForKey:方法内部:

首先会把参数key的第一个字母变成大写的。

然后根据这个Keykey按顺序去查找对应的setter方法,包括getKeykey:isKey:_key:,如果找到了对应的getter方法,就调用并返回属性的值(本质上还是读取成员变量的值)。

如果没有找到对应的getter方法,就会去查看accessInstanceVariablesDirectly方法的返回值(能否直接访问成员变量,默认返回YES),如果不能直接访问成员变量,就会报一个“找不到key”的错。

如果能直接访问成员变量,就会按顺序去查找对应的成员变量,包括_key_isKeykeyisKey,如果找到了对应的成员变量,就直接读取成员变量的值,如果没有找到对应的成员变量,就会报一个“找不到key”的错。

四、KVO常见面试题


1、通过KVC修改属性的话,会不会触发KVO?

答案:

会。

因为KVC的底层实现就是首先调用属性的setter方法设置,当然会触发KVO。

即便你不是修改属性,而是通过KVC直接修改成员变量——即类里面没有对应的setter方法,KVC也会触发KVO,因为KVC底层在修改成员变量的时候也调用了willChangeValueForKey:didChangeValueForKey:方法。

[self.person1 willChangeValueForKey:@"age"];
self.person1->_age = 26;
[self.person1 didChangeValueForKey:@"age"];
上一篇 下一篇

猜你喜欢

热点阅读