KVC之-setValue:forKey:方法实现原理与验证
KVC之-setValue:forKey:方法实现原理与验证
- (void)setValue:(id)value forKey:(NSString *)key
方法,实现原理与验证
功能:使用一个字符串标示符给一个对象的属性赋值.它支持普通对象和集合对象
这个方法的默认实现如下:
(1).首先去接收者(调用方法的那个对象)的类中查找与key相匹配的访问器方法(-set<Key>
),如果找到了一个方法,就检查它参数的类型,如果它的参数类型不是一个对象指针类型,但是只为nil
,就会执行setNilValueForKey:
方法,setNilValueForKey:
方法的默认实现,是产生一个NSInvalidArgumentException
的异常,但是你可以重写这个方法.如果方法参数的类是一个对象指针类型,就会简单的执行这个方法,传入对应的参数.如果方法的参数类型是NSNumber
或NSValue
的对应的基本类型,先把它转换为基本数据类,再执行方法,传入转换后的数据.**
(2).如果没有对应的访问器方法(setter
方法),如果接受者的类的+accessInstanceVariablesDirectly
方法返回YES
,那么就查找这个接受者的与key
相匹配的实例变量(匹配模式为_<key>
,_is<Key>
,<key>
,is<Key>
):比如:key
为age
,只要属性存在_age
,_isAge
,age
,isAge
中的其中一个就认为匹配上了,如果找到这样的一个实例变量,并且的类型是一个对象指针类型,首先released
对象上的旧值,然后把传入的新值retain
后的传入的值赋值该成员变量,如果方法的参数类型是NSNumber
或NSValue
的对应的基本类型,先把它转换为基本数据类,再执行方法,传入转换后的数据.
(3).如果访问器方法和实例变量都没有找到,执行setValue:forUndefinedKey:
方法,该方法的默认实现是产生一个 NSUndefinedKeyException
类型的异常,但是我们可以重写setValue:forUndefinedKey:方法
验证:
定义一个Person类:如下
@interface Person : NSObject
{
NSString *_name;
int _age;
NSString *_address;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic,assign) int age;
@end
@implementation Person
-(void)setName:(NSString *)name
{
NSLog(@"%s----------%@",__func__,name);
_name = name;
}
- (void) setAge:(int)age
{
_age = age;
NSLog(@"%s------%d",__func__,age);
}
- (int) age
{
NSLog(@"%s------%d",__func__,_age);
return _age;
}
- (NSString *) name
{
NSLog(@"%s----------%@",__func__,_name);
return _name;
}
@end
测试代码
1)验证: setValue:forKey:确实会调用-set<Key>方法
Person *p = [[Person alloc] init];
[p setValue:@"小明" forKey:@"name"];
输出结果
2015-08-15 20:56:56.975 company[1254:98490] -[Person setName:]----------小明
2015-08-15 20:56:56.975 company[1254:98490] -[Person setAge:]------10
2)验证:如果它的参数类型不是一个对象指针类型,但是只为nil
,就会执行setNilValueForKey:
方法,setNilValueForKey:
方法的默认实现,是产生一个NSInvalidArgumentException
的异常
测试代码[p setValue:nil forKey:@"age"];
运行结果:
2015-08-15 20:59:36.111 company[1300:100841]
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:
3)可以重写这个方法setNilValueForKey:
在Person类的实现文件中,重写setNilValueForKey:
- (void) setNilValueForKey:(NSString *)key
{
NSLog(@"%s",__func__);
}
`
再次运行,结果:
2015-08-15 21:29:21.167 company[528:6226] -[Person setNilValueForKey:]
4)验证如果方法的参数类型是NSNumber
或NSValue
的对应的基本类型,先把它转换为基本数据类,再执行方法,传入转换后的数据,测试代码
Person.m 文件中:
- (void) setAge:(int)age
{
_age = age;
NSLog(@"%s------%d",__func__,age);
}
测试方法中
[p setValue:@(10) forKey:@"age"];
执行结果
2015-08-15 21:54:23.477 company[607:15602] -[Person setAge:]------10
5)验证如果如果没有对应的访问器方法(setter
方法),如果接受者的类的+accessInstanceVariablesDirectly
方法返回YES
,那么就查找这个接受者的与key
相匹配的实例变量(匹配模式为_<key>
,_is<Key>
,<key>
,is<Key>
):比如:key
为age
,只要属性存在_age
,_isAge
,age
,isAge
中的其中一个就认为匹配上了,如果找到这样的一个实例变量,并且的类型是一个对象指针类型,首先released
对象上的旧值,然后把传入的新值retain
后的传入的值赋值该成员变量,如果方法的参数类型是NSNumber
或NSValue
的对应的基本类型,先把它转换为基本数据类,再执行方法,传入转换后的数据.
验证:+accessInstanceVariablesDirectly默认返回YES
测试代码
NSLog(@"%d",[Person accessInstanceVariablesDirectly]);
输出结果:
2015-08-15 22:05:22.646 company[782:21098] 1
在Person
类中分别使用
@interface Person : NSObject
{
// NSString *address;
// NSString *_address;
// 注意is后面第一个字母必须大写否则会产生NSUnknownKeyException异常
// NSString *isAddress;
NSString *_isAddress;
}
测试代码
NSLog(@"%d",[Person accessInstanceVariablesDirectly]);
[p setValue:@"金燕龙大厦" forKey:@"address"];
NSString *address = [p valueForKey:@"address"];
输出结果:
2015-08-15 22:05:22.646 company[782:21098] 金燕龙大厦
6)验证:如果访问器方法和实例变量都没有找到,执行setValue:forUndefinedKey:
方法,该方法的默认实现是产生一个 NSUndefinedKeyException
类型的异常,但是我们可以重写setValue:forUndefinedKey:
方法
测试代码:
[p setValue:@"美女" forKey:@"老婆"];
结果产生一个NSUnknownKeyException:
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Person 0x7fd0394a4c10> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key 老婆.'
在Person.m
文件中重写 - (void)setValue:(id)value forUndefinedKey:(NSString *)key
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
NSLog(@"%s",__func__);
NSLog(@"%@=%@",key,value);
}
再次运行程序输出结果:
2015-08-15 22:14:19.866 company[885:25268] -[Person setValue:forUndefinedKey:]
2015-08-15 22:14:19.866 company[885:25268] 老婆=美女
KVC之-setValue:forKey:方法实现原理与验证的更多相关文章
- setObject:forKey和setValue:forKey的区别
setObject:forKey: 是NSMutableDictionary类的方法 key参数类型可以是任意类型对象 ...
- 从[id setValue: forKey:]了解KVC
<Objective-C基础教程> P224页有详细介绍 下边是apple官网的简单介绍 和一个应用的例子. KVC就是Key-value coding,大意是允许通过一个Key来读写一个 ...
- iOS.KVC.setValue:forKey:
Foundation Framework 定义了 NSObject(NSKeyValueCoding), - (void)setValue:(id)value forKey:(NSString *)k ...
- KVC之-(id)valueForKey:(NSString *)key的实现原理与验证
KVC之-(id)valueForKey:(NSString *)key的实现原理与验证 2.-(id)valueForKey:(NSString *)key的实现原理与验证; #功能:使用一个字符串 ...
- Objective-c setObject:forKey:和setValue:forKey:的区别
setObject:forKey: 是NSMutableDictionary类的方法 key参数类型可以是任意类型对象 ...
- 字体属性设置(一):谷歌浏览器12px以下字体的显示;方法和原理
前言: chrome 谷歌浏览器默认的字体大小为16px:可以通过设置font-size来设置字体大小但是当设置到12px以下的时候字体大小不再改变:对于想设置其他大小的字体就很头疼,本文参考网上的方 ...
- Android ListView实现不同item的方法和原理分析
ListView实现不同item的方法和原理分析 一问题抛出Listview是android里面的重要组件,用来显示一个竖向列表,这个没有什么问题:但是有个时候列表里面的item不是一样的,如下图,列 ...
- JS跨域方法及原理
JS跨域分析判断 JS跨域:在不同域之间,JS进行数据传输或通信.比如ajax向不同的域请求数据.JS获取iframe中的页面中的值(iframe内外不同域) 只要协议.端口.域名有一个不同则 ...
- android中获取root权限的方法以及原理(转)
一. 概述 本文介绍了android中获取root权限的方法以及原理,让大家对android 玩家中常说的“越狱”有一个更深层次的认识. 二. Root 的介绍 1. Root 的目的 可以让我们拥有 ...
随机推荐
- prometheus,alertmanager 报警配置详解
vim prometheus.yml global: scrape_interval: 15s external_labels: monitor: 'codelab-monitor' scrape_c ...
- 如何打造一款可靠的WAF
之前写了一篇<WAF防御能力评测及工具>,是站在安全运维人员选型WAF产品的角度来考虑的(优先从测试角度考虑是前职业病,毕竟当过3年游戏测试?!).本篇文章从WAF产品研发的角度来YY如何 ...
- SQL_MODE设置讲解
SQL_MODE可能是比较容易让开发人员和DBA忽略的一个变量,默认为空.SQL_MODE的设置其实是比较冒险的一种设置,因为在这种设置下 可以允许一些非法操作,比如可以将NULL插入NOT NULL ...
- python matplotlib.pyplot学习记录
matplotlib是python中很强大的绘图工具,在机器学习中经常用到 首先是导入 import matplotlib.pyplot as plt plt中有很多方法,记录下常用的方法 plt.p ...
- 隐藏系统EFI分区Z盘
找到C:\Windows\System32\cmd.exe程序, 右键单击cmd 选择以管理员身份运行, 打开命令提示符,输入以下命令(不区分大小写)DiskPart回车List空格volume回车s ...
- LR中判断HTTP返回状态
有天,有个人问:我在做b/s测试,请问如何保存从服务器传回来的http头的信息,怎么能得到http状态,和状态200进行比较? 后来,我给出的代码如下: Action() {int i; // [WC ...
- js 创建数组方法以及区别
示例代码: <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF ...
- openSession() 与 getCurrentSession() 有何不同和关联呢?
在 SessionFactory 启动的时候, Hibernate 会根据配置创建相应的 CurrentSessionContext ,在getCurrentSession() 被调用的时候,实际被执 ...
- 获取SQL Server的安装时间
近期安装SQL Server 2014时.还没有正式的License,仅仅能试用3个月.想知道什么时候到期,就要知道SQL Server 2014是什么时候安装的.假设你没有特意记录安装日期(实际大部 ...
- 【微信小程序】退款功能教程(含申请退款和退款回调)
1.一定要区分小程序和公众号的退款,唯一的区别就是 appid不一样,其他的都是一样的. 不废话,直接写代码了啊. 放大招!!! 然后,需要注意的:最好是把证书放在下面的php的同级或者下级. 证书的 ...