Objective - C 底层

Objective - C KVC

2020-04-30  本文已影响0人  爱玩游戏的iOS菜鸟

(一)KVC (Key-Value Coding)

KVC,即键值编码,通过key来访问属性

(1)常见的API
(2)访问属性常规用法
  1. 设置
  self.person = [[ZQPerson alloc]init];
  //setValue: forKey:方法
  [self.person setValue:@10 forKey:@"age"];
  self.person.dog = [[ZQDog alloc] init];
 //setValue: forKeyPath:方法
  [self.person setValue:@20 forKeyPath:@"dog.height"];
  1. 访问(同理)
  //输出10 10
  NSLog(@"%@ %@",[self.person valueForKey:@"age"],[self.person valueForKeyPath:@"dog.height"])
疑问:通过KVC设置成员变量的值,会触发KVO吗?
- (void)viewDidLoad {
    [super viewDidLoad];

    self.person = [[ZQPerson alloc]init];

    [self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    [self.person setValue:@20 forKey:@"age"];

}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    NSLog(@"%@  %@  %@",keyPath,object,change);
}

-(void)dealloc{
    [self.person removeObserver:self forKeyPath:@"age"];
}

输出结果:会触发!

age  <ZQPerson: 0x60000026a5e0>  {
    kind = 1;
    new = 20;
    old = 0;
}

到底是什么原因呢?

(3)设值原理

①设置ZQPerson的_age成员变量为私有,添加两个方法(如下),重新运行

//ZQPerson.h
@interface ZQPerson : NSObject{
    int _age;
}

-(void)setAge:(int)age;

-(void)_setAge:(int)age;

@end

//ZQPerson.m
@implementation ZQPerson

-(void)setAge:(int)age{
    _age = age;
    NSLog(@"setAge:%d",age);
}

-(void)_setAge:(int)age{
    _age = age;
    NSLog(@"其次找这里");
}

@end

输出结果:

setAge:20

age  <ZQPerson: 0x600003304ac0>  {
    kind = 1;
    new = 20;
    old = 0;
}

②接着注释掉setAge,再次执行:

_setAge:20

age  <ZQPerson: 0x600002b442d0>  {
    kind = 1;
    new = 20;
    old = 0;
}

我们可以得出结论一:
setValue: forKey:方法首先会查找setKey,_setKey的优先顺序先查找方法

④我们删除所有的set相关方法,重新运行:

age  <ZQPerson: 0x6000015d5cc0>  {
    kind = 1;
    new = 20;
    old = 0;
}

KVO依然会触发!!!

⑤继续,我们重写+(BOOL)accessInstanceVariablesDirectly方法,返回NO,发现报错了valueForUndefinedKey

reason: '[<ZQPerson 0x6000029ac7f0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key age.'

我们可以得出结论二:
如果没有找到方法,会判断该方法是否允许查找成员变量,不允许的情况下直接报错

⑥接下来,我们将+(BOOL)accessInstanceVariablesDirectly返回YES,添加_age,_isAge,age,isAge四个成员变量:

@interface ZQPerson : NSObject{
    int _age;
    int _isAge;
    int age;
    int isAge;
}

@end

运行结果:

接下来不管是怎么注释,或者更换顺序,赋值也是按照这个优先顺序给成员变量赋值
我们可以得出结论三:
若方法是否允许查找成员变量,则会按照_age,_isAge,age,isAge的优先顺序给成员变量赋值,如果找不到,还是会报错valueForUndefinedKey

下面对上面的验证流程做一个总结:


accessInstanceVariablesDirectly方法的默认返回值是YES

只要通过setValue: forKey:的方法,只要赋值成功了,就会触发KVO(自己可以去代码验证)
可能在赋值过程内部,调用了willChangeValueForKey:didChangeValueForKey:,可以通过重写这两个方法,发现确实会调用这两个方法(验证流程省略)

(4)取值原理
验证流程省略
上一篇下一篇

猜你喜欢

热点阅读