OC底层学习-KVC


KVC的全称是Key-Value Coding,俗称"键值编码",可以通过一个key来访问某个属性.

  • 常见的API:
    1. - (void)setValue:(nullable id)value forKey:(NSString *)key 设置值
    2. - (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath设置值(可以根据路径查找赋值)
    3. - (nullable id)valueForKey:(NSString *)key; 取值
    4. - (nullable id)valueForKeyPath:(NSString *)keyPath;取值

1 KVC的本质

1.1 通过KVC赋值会不会触发KVO?

示例代码:

//创建一个person类
@interface Person : NSObject

/** <#des#> */
@property (nonatomic, assign) int age;
@end
@implementation Person

@end

//创建一个监听者类
@interface GYObserver : NSObject

@end
@implementation GYObserver

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

//测试代码
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[Person alloc] init];
        GYObserver * observer = [[GYObserver alloc] init];
        
        //给perosn设置KVO监听
        [person addObserver:observer forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
        
        //使用KVC赋值
        [person setValue:@20 forKey:@"age"];
        
        //使用完之后,注销KVO
        [person removeObserver:observer forKeyPath:@"age"];
    }
    return 0;
}

打印结果:
在这里插入图片描述
这说明使用KVC赋值,是会触发KVO的(因为KVO的本质是替换了set方法的实现),如此说明KVC赋值肯定调用了属性的set方法,下面我们验证下,我们重写age的set方法看看

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

在这里插入图片描述

结论:结果表明KVC赋值,确实会调用属性的set方法。如果类中没有set方法,也会触发KVO的,没有set方法的结论和分析,可参考考下面的分析过程和面试题1的结论

1.2 setValue:forKey:的原理

  1. 首先回去找setAge()或则_setAge()方法,两者同时存在的同时执行setAge()方法
    示例代码:
```objectivec
//创建一个person类
@interface Person : NSObject
@end
@implementation Person
- (void)setAge:(int)age {
    
    NSLog(@"setAge---- %d", age);
}

- (void)_setAge:(int)age {
    NSLog(@"_setAge---- %d", age);
}
@end

//测试代码
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[Person alloc] init];
        //使用KVC赋值
        [person setValue:@20 forKey:@"age"];
    }
    return 0;
}

当我注释_setAge()方法时,保留setAge()方法时,运行结果:setAge---- 20
当我注释setAge()方法是,保留_setAge()方法时,运行结果:_setAge---- 20

由此可见,setValue:forKey:第一步首先回去寻找属性的setXXX和_setXXX()方法,传递参数调用方法

  1. 如果上述两个方法都没有找到,那会去查看+ (BOOL)accessInstanceVariablesDirectly( 是否能够直接访问成员变量->默认返回值YES)方法的返回值。如果返回NO就不允许直接访问成员变量,然后调用setValue:forUndefinedKey:方法抛出异常:NSUnknownKeyException(不认识这个key),如果返回YES,回去直接按照顺序寻找以下成员变量: _key、_isKey、key、isKey, **如果找到了成员变量直接赋值,**否则,那还是会调用setValue:forUndefinedKey:抛出异常, 接下来我们验证下
@interface Person : NSObject
{
    @public
    int _age;
    int _isAge;
    int age;
    int isAge;
}

@implementation Person

//表示是否能够直接访问成员变量 Directly直接
//默认返回值YES
+ (BOOL)accessInstanceVariablesDirectly {
    return YES;
}
@end

直接运行结果表示第一会找_age这个名称的成员变量赋值
在这里插入图片描述
注释_age之后的运行结果
在这里插入图片描述
注释_age_isAge之后的运行结果
在这里插入图片描述
注释_age_isAgeage之后的运行结果
在这里插入图片描述

全部注释之后,直接抛出异常
在这里插入图片描述

  1. setValue:forKey:的原理图
    在这里插入图片描述

2. 面试题

2.1 通过KVC修改属性会触发KVO么?

  • 会触KVO (不管有没有set方法,即使KVC直接修改成员变量,也会触发KVO,说明KVC的内部可能做了KVO监听的一些操作)
  • 实质上调用KVC赋值,KVC会调用对象的willChangeValueForKey:didChangeValueForKey:这两个方法,相当于是手动触发KVO。
  • 我们在上述的person类中重写这两个方法,然后使用KVO监听,并使用KVC给成员变量赋值
  • 打印结果:
    在这里插入图片描述

2.2 KVC的赋值和取值过程是怎么样的?原理是什么?

  • 赋值过程:

    1. 首先会去找setAge()或则_setAge()方法,两者同时存在的同时执行setAge()方法,如果找到方法直接赋值
    2. 如果没有找到这两个方法,那会去查看+ (BOOL)accessInstanceVariablesDirectly( 是否能够直接访问成员变量->默认返回值YES)方法的返回值。如果返回NO就不允许直接访问成员变量,然后调用setValue:forUndefinedKey:方法抛出异常:NSUnknownKeyException(不认识这个key),如果返回YES,回去直接按照顺序寻找以下成员变量: _key、_isKey、key、isKey, **如果找到了成员变量直接赋值,**否则,那还是会调用setValue:forUndefinedKey:抛出异常
    3. 赋值的原理流程图在在上面
  • 取值过程

    1. 首先会去寻找getKey、key、isKey、_key这个四个get方法去,如果找到方法,就直接返回值
    2. 如果没有找到这几个方法,那会去查看+ (BOOL)accessInstanceVariablesDirectly( 是否能够直接访问成员变量->默认返回值YES)方法的返回值。如果返回NO就不允许直接访问成员变量,然后调用setValue:forUndefinedKey:方法抛出异常:NSUnknownKeyException(不认识这个key),如果返回YES,回去直接按照顺序寻找以下成员变量: _key、_isKey、key、isKey, **如果找到了成员变量直接赋值,**否则,那还是会调用setValue:forUndefinedKey:抛出异常
    3. 取值的原理流程图如下:
      在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值