KVC 

 一直没仔细看过KVC的用法,想当然的认为可以在NSObject对象中存入任意键值对,结果使用时碰到问题了。

  一个简单的位移动画:

CAKeyframeAnimation *keyPosi=[CAKeyframeAnimation animationWithKeyPath:@"position"];
keyPosi.path=path.CGPath;
keyPosi.delegate=self;
[label.layer addAnimation:keyPosi forKey:@"x"];

  我想要在动画结束后把UILabel从屏幕上移除,于是加上动画结束后的回调:

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{

}

  然而看上去从回调方法的参数中似乎无法得到UILabel对象(也许提供的有api可以获得UIlabel,希望各位看官不吝赐教),如果只是为了在这里得到UILabel对象而去设置一个全局变量指针指向它感觉没必要,这时候我想起了KVC,于是在创建动画的时候加上一句:

[keyPosi setValue:label forKey:@"xx"];

  这样在动画结束的回调方法中可以通过anim参数获得UILabel对象了:

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{

if (flag) {

UIView *vie=[anim valueForKey:@"xx"];

[vie removeFromSuperview];

}

}

  效果已经实现,然而KVC是这样使用的吗?再看一个例子:

NSObject *obj = [[NSObject alloc] init];
[obj setValue:@"asd" forKey:@"xx"];

  运行报错:

*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<NSObject 0xf65f090> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key xx.'

  原来setValue:forKey、setValue:forKeyPath的key(Path)参数不是随便写的,必须是类中定义的成员变量名(或者实例方法),这篇文章写的很清楚:http://www.cnblogs.com/jay-dong/archive/2012/12/13/2815778.html。话说回来,为啥前面CAKeyframeAnimation使用的setValue:forKey的key可以成功设置?类里肯定没有xx这个成员变量。继续查资料发现有个方法setValue:forUndefinedKey:,

setValue:forUndefinedKey:
Invoked by setValue:forKey: when it finds no property for a given key.
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
Discussion
Subclasses can override this method to handle the request in some other way. The default implementation raises an NSUndefinedKeyException.

  当setValue方法的key参数在类中找不到对应成员时,会调用这个方法,重写它可以阻止抛出NSUndefinedKeyException异常。

  这样看来,CAKeyFrameAnimation类(或者它的父类)应该是重写了setValue:forUndefinedKey:,然而方法里是怎样处理从而使得valueForKey可以正确取到值呢?

associative

  objective-c的扩展机制有两个特性:category和associative。category扩展类别,associative扩展属性。使用associative需要导入<objc/runtime.h>头文件。

  利用category扩展NSObject类别,利用associative和setValue:forUndefinedKey:让NSObject能够存入任意键值对。

#import <objc/runtime.h>
@interface NSObject (KVC)
- (id)valueForUndefinedKey:(NSString *)key;
- (void)setValue:(id)value forUndefinedKey:(NSString *)key;
@end @implementation NSObject(KVC)
- (id)valueForUndefinedKey:(NSString *)key{
return objc_getAssociatedObject(self, key);
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
if ([value isKindOfClass:[NSString class]]) {
objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_COPY_NONATOMIC);
}else{
objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
} }
@end

  现在再试下:

NSObject *obj = [[NSObject alloc] init];
[obj setValue:@"asd" forKey:@"xx"];
NSLog(@"%@",[obj valueForKey:@"xx"]);

  可以输出了吧。

更正!

   对各位说声抱歉,之前犯了一个想当然的错误,setValue:forUndefinedKey:和valueForUndefinedKey:两个方法中的key不能直接传给setAssociatedObject和getAssociatedObject方法,如下所示的做法是错误的:

NSString *a=[NSString stringWithFormat:@"hello"];
NSString *b=[NSString stringWithFormat:@"hello"];
[self setValue:@"cannotfind" forKey:a];
NSString *result=[self valueForKey:b];
NSLog(@"%@",result);

  这里result是空,因为a、b是两个不同指针,setAssociatedObject和getAssociatedObject使用了不同的key。正确的做法是定义全局静态变量作为key:

static NSString *kHello=@"hello";

[self setValue:@"canfind" forKey:kHello];
NSString *result=[self valueForKey:kHello];
NSLog(@"%@",result);

  或者这样:

static char kHello;

- (id)valueForUndefinedKey:(NSString *)key{
const void *newkey=nil;
if ([key isEqualToString:@"hello"]) {
newkey=&kHello;
}else{
NSAssert(false, @"undefined key");
}
return objc_getAssociatedObject(self, newkey);
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
const void *newkey=nil;
if ([key isEqualToString:@"hello"]) {
newkey=&kHello;
}else{
NSAssert(false, @"undefined key");
} if ([value isKindOfClass:[NSString class]]) {
objc_setAssociatedObject(self, newkey, value, OBJC_ASSOCIATION_COPY_NONATOMIC);
}else{
objc_setAssociatedObject(self, newkey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
} }

(bug更正)利用KVC和associative特性在NSObject中存储键值的更多相关文章

  1. 利用js对象的特性,去掉数组中的重复项

  2. 利用js取到下拉框中选择的值

    现在的需求是:下拉框中要是选择加盟商让其继续选择学校,要是选择平台管理员则不需要选择学校.隐藏选择下拉列表. 选择枚举值: /// <summary> /// 平台角色 /// </ ...

  3. 利用pandas映射替换两个字典中的映射值

    在公司处理报表,中英文映射表与数值表替换 import pandas as pd data = { "a":"值一", "b":" ...

  4. iOS - KVC 键值编码

    1.KVC KVC 是 Key-Value Coding 的简写,是键值编码的意思,属于 runtime 方法.Key Value Coding 是 cocoa 的一个标准组成部分,是间接给对象属性设 ...

  5. [原创]obj-c编程16:键值编码(KVC)

    原文链接:obj-c编程16:键值编码(KVC) 我们可以借助obj-c中的键值编码(以后简称KVC,Key-Value Coding)来存取类的属性,通过指定所要访问的属性名字符串标示符,可以使用存 ...

  6. obj-c编程16:键值编码(KVC)

    我们可以借助obj-c中的键值编码(以后简称KVC,Key-Value Coding)来存取类的属性,通过指定所要访问的属性名字符串标示符,可以使用存取器方法来获取或设置类的属性.下面的例子,借助于K ...

  7. ios中键值编码kvc和键值监听kvo的特性及详解

    总结: kvc键值编码  1.就是在oc中可以对属性进行动态读写(以往都是自己赋值属性)           2. 如果方法属性的关键字和需要数据中的关键字相同的话                  ...

  8. 利用atimicInteger cas的特性实现一个锁

    利用atimicInteger cas的特性实现一个锁 主要是使用的是 atomicIntegerAPI 的compareAndSet()方法,让线程不在阻塞,获取不到直接失败. 我们先定义一个异常类 ...

  9. 《芒果TV》UWP版利用Windows10通用平台特性,率先支持Xbox One平台

    在Windows开发者中心开放提交Xbox平台应用之后,<芒果TV>UWP版迅速更新v3.1.2版,通过升级兼容目标,利用Windows10通用平台特性,率先覆盖Xbox平台用户. 芒果T ...

随机推荐

  1. mybatis0203 一对一查询 resultMap实现

    resultType实现的时候先要确定po类(数据库类)满不满足要求,如果不满足就要自定义一个pojo类(工具类). resultMap提供一对一关联查询的映射和一对多关联查询映射,一对一映射思路:将 ...

  2. MapReduce中的Join算法

    在关系型数据库中Join是非常常见的操作,各种优化手段已经到了极致.在海量数据的环境下,不可避免的也会碰到这种类型的需求,例如在数据分析时需要从不同的数据源中获取数据.不同于传统的单机模式,在分布式存 ...

  3. 关于cocos2d-x精灵加亮及变灰效果

    //根据现有CCSprite,变亮和变灰 static CCSprite* graylightWithCCSprite(CCSprite* oldSprite,bool isLight) { //CC ...

  4. plupload使用指南(转)

    转自http://www.cnblogs.com/2050/p/3913184.html 现在随着html5技术的逐渐推广和普及,再去使用以flash为上传手段的SWFUpload显然就有点过时了,毕 ...

  5. linux lsof nmap netstat

    lsof -i :22    # 显示22端口当前运行的程序 lsof -c ssh  # 显示ssh进程打开的文件 lsof -p 2120  #显示进程id2120打开的文件   nmap -sP ...

  6. 利用jQuery npoi插件 asxh一般处理文件实现excel的下载

    最近开发的过程中遇到这么一个问题,利用ajax和ashx文件实现下载功能.发现代码调试走完之后并没有弹出下载框. 研究了一段时间之后发现解决这种问题有两种方法,1.ajax获取数据集在前台做处理实现导 ...

  7. 用Module元素实现SharePoint Webpart Page的自动生成

    最近研发的项目中开发了很多的WebPart,每次部署这些WebPart到新环境中总是很麻烦,因为自己要新创建WebpartPage,同时还要把这些WebPart放到指定的WebPart页中去: 为了方 ...

  8. WisDom.Net 框架设计(四) 用户安全

    WisDom.Net  ----用户安全 1.用户单机登录 正如其名这里要求其实就是显示用户只能在一台电脑上登录.防止多处登录,这里简单的说一下实现原理,我们在这里使用session +cookie ...

  9. Windows Socket五种I/O模型

    转载:http://www.cnblogs.com/tianzhiliang/archive/2010/08/31/1813637.html 如果你想在Windows平台上构建服务器应用,那么I/O模 ...

  10. c#equals相关

    1.==是直接比较值类型的值或引用类型的引用地址,但==不能用于struct,struct只能用equals来比较.==一般情况下与object.equals得到的结果是相等的. 2.Referenc ...