OC对象 - KVC

OC对象 - KVC

俗称"键值编码",可以通过一个key来访问某个属性

1. 常用API

    • (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
    • (void)setValue:(id)value forKey:(NSString *)key;
    • (id)valueForKeyPath:(NSString *)keyPath;
    • (id)valueForKey:(NSString *)key;

2. 原理

2.1 setValue:forKey:的原理

  • 在执行setValue:forKey:的时候
  • 按照setKey_setKey:顺序查找方法
  • 找到方法就传递参数,调用方法
  • 没找到方法,会看一下 accessInstanceVariablesDirectly方法的 return(默认return YES)
  • 如果 accessInstanceVariablesDirectly方法的 return NO,就会走setValue:forUndefinedKey:,并抛出异常NSUnknownKeyException
  • 否则的话
  • 此时会按照_key、_isKey、key、isKey的顺序查找成员变量,找到了就直接赋值

2.2 验证setValue:forKey:

2.2.1 查找方法阶段

  • 我们在ZSXPerson.m中实现下这两个方法
objectivec 复制代码
@implementation ZSXPerson

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

- (void)_setAge:(int)age {
    NSLog(@"%s", __func__);
    _age = age;
}

@end

此时可以看到执行的是setAge:方法

  • 验证查找_setAge:方法

要验证_setAge:方法,我们不能再用@property 来声明 age 属性,因为@property 会自动帮我们生成 get、set 方法

  • ZSXPerson 我们去掉@property声明的age属性,.m文件里面实现_setAge:方法,然后同样使用setValue:forKey:来给 age 赋值
less 复制代码
@interface ZSXPerson : NSObject	

@end

@implementation ZSXPerson

//- (void)setAge:(int)age {
//    NSLog(@"%s", __func__);
//}

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

@end
  • 我们发现_setAge:方法确实执行了
  • 如果我们把setAge:方法再打开,会发现setAge:是比_setAge:方法优先被查找调用的

2.2.2 查找成员变量阶段

2.2.2.1 验证成员变量按顺序查找
  • 我们假设ZSXPerson有这样几个成员变量。(setAge:(int)age_setAge:(int)age需要去掉,否则会优先找方法)
objectivec 复制代码
@interface ZSXPerson : NSObject	{
    @public
    int _age;
    int _isAge;
    int age;
    int isAge;
}

@end

@implementation ZSXPerson

//- (void)setAge:(int)age {
//    NSLog(@"%s", __func__);
//}

//- (void)_setAge:(int)age {
//    NSLog(@"%s", __func__);
//}

@end
  • _age

  • _isAge

  • age
  • isAge

验证了 成员变量按顺序查找

2.3 setValue:forKey: 触发KVO监听

有set方法

我们清楚KVO的原理,以及KVCsetValue:forKey:原理,已经能确定是会触发KVO监听的

没有set方法

我们主要验证下没有set方法的情况下,是否会触发KVO监听

  • 我们刚刚ZSXPerson的.m文件中,已经注释掉相应的set方法
  • 这时候给ZSXPerson的实例对象添加KVO监听 age 值变化

可以看到 KVO 监听也是会触发的

我们已经没有任何 set 方法了,但是 KVO 监听依然触发了。这是因为 KVC 内部本身会触发 keyPath 值变化通知

2.2.2.2 accessInstanceVariablesDirectly

我们尝试accessInstanceVariablesDirectly返回NO

此时它不会查找成员变量,因此找不到对应的 key 赋值,抛异常了

2.3 valueForKey:的原理

  • 在执行valueForKey:的时候
  • 按照getKeykeyisKey_key顺序查找方法
  • 找到方法就传递参数,调用方法
  • 没找到方法,会看一下 accessInstanceVariablesDirectly方法的 return(默认return YES)
  • 如果 accessInstanceVariablesDirectly方法的 return NO,就会走setValue:forUndefinedKey:,并抛出异常NSUnknownKeyException
  • 否则的话
  • 此时会按照_key、_isKey、key、isKey的顺序查找成员变量,找到了就直接赋值

2.4 验证valueForKey:

2.4.1 查找方法阶段

  • 分别创建getKeykeyisKey_key四个方法
  • 我们直接给实例对象person的age赋值,然后使用valueForKey:获取age

valueForKey:按顺序查找方法,最后查不到对应方法抛异常了

2.4.1 查找成员变量阶段

找不到方法,接下来按顺序查找成员变量

  • 我们创建_key、_isKey、key、isKey几个成员变量
  • 先给几个成员变量赋不同的值,然后使用valueForKey:获取age

valueForKey:按顺序查找成员变量,最后查不到对应方法抛异常了

@oubijiexi

相关推荐
琥珀色糖8 小时前
Linux守护进程
linux·终端·守护进程·会话·进程组
开心大爆炸12 小时前
xrdp 连接 登录对话框输入密码后闪退
linux·运维·服务器
oushaojun212 小时前
大厂 C++ 面试:深度剖析 Linux Signal 信号机制(转)
linux
苏生Susheng13 小时前
【软件实施】Linux系统Shell脚本教程
linux·运维·服务器·chrome·spring boot·学习·实施
Ruiery13 小时前
Linux 6.6内核 IOMMU 深度解析(七):DMA API 与 IOMMU 集成 — 从 dma_map_single 到 iommu_map
linux·运维·服务器
分支预测失败13 小时前
RISC-V AIA 中断架构实战:从 PLIC 到 APLIC 与 IMSIC 的迁移
linux·后端
GeW13 小时前
RHCE学习拿证计划:锁定核心考点,实现备考效率最大化
linux
snow@li14 小时前
服务器运维:Alibaba Cloud Linux 4 LTS 64位 根目录全景深度解析文章
linux·运维·服务器
苏生Susheng14 小时前
【软件实施】Linux企业运维常用命令手册
java·linux·运维·服务器·springboot·springcloud·软件实施
额额额对了14 小时前
Linux 进程间通信(IPC)核心机制:消息队列、共享内存与信号量详解
linux·网络·网络协议