KVC之-setValue:forKey:方法实现原理与验证

- (void)setValue:(id)value forKey:(NSString *)key方法,实现原理与验证

功能:使用一个字符串标示符给一个对象的属性赋值.它支持普通对象和集合对象
这个方法的默认实现如下:

(1).首先去接收者(调用方法的那个对象)的类中查找与key相匹配的访问器方法(-set<Key>),如果找到了一个方法,就检查它参数的类型,如果它的参数类型不是一个对象指针类型,但是只为nil,就会执行setNilValueForKey:方法,setNilValueForKey:方法的默认实现,是产生一个NSInvalidArgumentException的异常,但是你可以重写这个方法.如果方法参数的类是一个对象指针类型,就会简单的执行这个方法,传入对应的参数.如果方法的参数类型是NSNumberNSValue的对应的基本类型,先把它转换为基本数据类,再执行方法,传入转换后的数据.**


(2).如果没有对应的访问器方法(setter方法),如果接受者的类的+accessInstanceVariablesDirectly方法返回YES,那么就查找这个接受者的与key相匹配的实例变量(匹配模式为_<key>,_is<Key>,<key>,is<Key>):比如:keyage,只要属性存在_age,_isAge,age,isAge中的其中一个就认为匹配上了,如果找到这样的一个实例变量,并且的类型是一个对象指针类型,首先released对象上的旧值,然后把传入的新值retain后的传入的值赋值该成员变量,如果方法的参数类型是NSNumberNSValue的对应的基本类型,先把它转换为基本数据类,再执行方法,传入转换后的数据.



(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)验证如果方法的参数类型是NSNumberNSValue的对应的基本类型,先把它转换为基本数据类,再执行方法,传入转换后的数据,测试代码
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>):比如:keyage,只要属性存在_age,_isAge,age,isAge中的其中一个就认为匹配上了,如果找到这样的一个实例变量,并且的类型是一个对象指针类型,首先released对象上的旧值,然后把传入的新值retain后的传入的值赋值该成员变量,如果方法的参数类型是NSNumberNSValue的对应的基本类型,先把它转换为基本数据类,再执行方法,传入转换后的数据.
验证:+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:方法实现原理与验证的更多相关文章

  1. setObject:forKey和setValue:forKey的区别

    setObject:forKey: 是NSMutableDictionary类的方法                              key参数类型可以是任意类型对象             ...

  2. 从[id setValue: forKey:]了解KVC

    <Objective-C基础教程> P224页有详细介绍 下边是apple官网的简单介绍 和一个应用的例子. KVC就是Key-value coding,大意是允许通过一个Key来读写一个 ...

  3. iOS.KVC.setValue:forKey:

    Foundation Framework 定义了 NSObject(NSKeyValueCoding), - (void)setValue:(id)value forKey:(NSString *)k ...

  4. KVC之-(id)valueForKey:(NSString *)key的实现原理与验证

    KVC之-(id)valueForKey:(NSString *)key的实现原理与验证 2.-(id)valueForKey:(NSString *)key的实现原理与验证; #功能:使用一个字符串 ...

  5. Objective-c setObject:forKey:和setValue:forKey:的区别

    setObject:forKey: 是NSMutableDictionary类的方法                               key参数类型可以是任意类型对象           ...

  6. 字体属性设置(一):谷歌浏览器12px以下字体的显示;方法和原理

    前言: chrome 谷歌浏览器默认的字体大小为16px:可以通过设置font-size来设置字体大小但是当设置到12px以下的时候字体大小不再改变:对于想设置其他大小的字体就很头疼,本文参考网上的方 ...

  7. Android ListView实现不同item的方法和原理分析

    ListView实现不同item的方法和原理分析 一问题抛出Listview是android里面的重要组件,用来显示一个竖向列表,这个没有什么问题:但是有个时候列表里面的item不是一样的,如下图,列 ...

  8. JS跨域方法及原理

        JS跨域分析判断 JS跨域:在不同域之间,JS进行数据传输或通信.比如ajax向不同的域请求数据.JS获取iframe中的页面中的值(iframe内外不同域) 只要协议.端口.域名有一个不同则 ...

  9. android中获取root权限的方法以及原理(转)

    一. 概述 本文介绍了android中获取root权限的方法以及原理,让大家对android 玩家中常说的“越狱”有一个更深层次的认识. 二. Root 的介绍 1. Root 的目的 可以让我们拥有 ...

随机推荐

  1. std::string 用法

    string类的构造函数:string(const char *s); //用c字符串s初始化string(int n,char c); //用n个字符c初始化 string类的字符操作:const ...

  2. Cisco路由技术基础知识详解

    第一部分 请写出568A的线序(接触网络第一天就应该会的,只要你掐过,想都能想出来) .网卡MAC地址长度是(  )个二进制位(16进制与2进制的换算关系,只是换种方式问,不用你拿笔去算) A.12  ...

  3. CKFinder根据用户设置权限,不同用户有自己的私有的、独立的文件夹

    CKFinder 默认情况下多个用户共用同一个图片目录.所有上传的图片和Flash全部保存在同一个文件夹(默认为 userfiles)内.     现在想实现:     第1个需求:不同用户有自己的私 ...

  4. npm install 报错:node-pre-gyp ERR! 问题解决

    npm install报错问题解决 问题: E:\CodeSpace\GitlabTest\desktop>npm install > lifeccp-desktop@1.1.9 post ...

  5. vue - .babelrc

    描述:bable-es2015以及babel本身组件在新版本要求的外部配置文件. { "presets": [ ["env", { "modules& ...

  6. unity3d格式的导出与加载

    http://blog.csdn.net/nateyang/article/details/7567831 1.导出.unity3d格式资源: http://game.ceeger.com/Scrip ...

  7. Google 收购 Android 十周年 全面解读Android现状

    --訪传智播客Android学科教学总监传智·平一指 Android以前是一家创立于旧金山的公司的名字,该公司于2005年8月份被Google收购,并从此踏上了飞速发展的道路.经过十年的发展,它已经发 ...

  8. python能够执行,但编译第三包遇到 python.h no such file or directory

    python能够执行,但编译第三包遇到 python.h no such file or directory 这个问题是由于没有安装python-devel, 安装此包就能够解决次问题,在Linux下 ...

  9. WKWebView的使用

    代码地址如下:http://www.demodashi.com/demo/13431.html 前言 最近项目中的UIWebView被替换为了WKWebView,因此来总结一下WKWebView的使用 ...

  10. Vue学习小结

    ES6 let完全可以取代var const声明一个只读的常量 箭头函数:可以绑定this对象,大大减少了显式绑定this对象的写法(call.apply.bind) 函数绑定(function bi ...