runtime学习之- 关联(association),在分类中添加属性!

一提到runtime,很多人都会产生莫名的恐惧(比如我。。。)

但事实上,runtime有相当一部分内容很简单、很好用,比如今天要讲的关联。


在<objc/runtime.h>中,有三个和它有关的方法(是的,一共就三个):

objc_setAssociatedObject

objc_getAssociatedObject

objc_removeAssociatedObjects


顾名思义,三个分别是设置关联、获取关联、移除关联。下面我们就分别来说一下。


1. objc_setAssociatedObject

来看一下函数原型:void objc_setAssociatedObject(id object,constvoid *key,id value,objc_AssociationPolicy policy)

它没有返回值,共有四个参数:object 是关联的源对象,key 是关联的关键字,value 是关联的值,policy 是关联策略。

需要注意的是,key 是一个 const 的常量,而且每一个关联的 key 必须是唯一的。

关联策略 objc_AssociationPolicy 是一个枚举类型,一共有五种:


意思很好懂,看名字就知道了,就不多说了。


2. objc_getAssociatedObject

来看一下函数原型:id objc_getAssociatedObject(id object,constvoid *key)

返回类型为 id,返回的就是关联的那个值。有两个参数,和上面的一样,object 是关联的源对象,key 是关联的关键字。

注意,这个 key 必须和刚才的 key 一样才能获取到正确的值。


3. objc_removeAssociatedObject

来看一下函数原型:void objc_removeAssociatedObjects(id object)

它没有返回值,只有一个参数 object,是关联的源对象。意思就是移除 object 的所有关联。

注意,是移除所有关联。这个函数的本意是使一个对象回到“原始状态”,通常不会用这个方法,因为它会把你在别的地方加入的关联也一起移除。那怎么办呢?


还记得怎么设置关联吗?void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)

第三个参数 value 传 nil 就是移除关联了,这只会移除指定 key 的关联而不是移除所有关联。


下面我们举个栗子,为了体现出关联的强大,我们在MRC下实践:

[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];  
  2. static char overviewKey;  
  3. NSArray *array = [[NSArray alloc] initWithObjects:@"lala", nil nil];  
  4. NSString *overview = [[NSString alloc] initWithString:@"I'm associatedObject."];  
  5.   
  6. // 1  
  7. objc_setAssociatedObject(array, &overviewKey, overview, OBJC_ASSOCIATION_COPY);  
  8. NSString *associatedObject = objc_getAssociatedObject(array, &overviewKey);  
  9. NSLog(@"1 - associatedObject: %@", associatedObject);  
  10.   
  11. // 2  
  12. [overview release];  
  13. associatedObject = objc_getAssociatedObject(array, &overviewKey);  
  14. NSLog(@"2 - associatedObject: %@", associatedObject);  
  15.   
  16. // 3  
  17. objc_setAssociatedObject(array, &overviewKey, nil, OBJC_ASSOCIATION_ASSIGN);  
  18. associatedObject = objc_getAssociatedObject(array, &overviewKey);  
  19. NSLog(@"3 - associatedObject: %@", associatedObject);  
  20.   
  21. [array release];  
  22. [pool drain];  

我们先定义一个 key。

然后创建一个 array,它就是关联的源对象。

然后我们创建一个字符串,它就是要关联到源对象上的那个值。

然后我们分三步来说:

1. 设置关联。输出结果为 “1 - associatedObject: I'm associatedObject.”

这说明我们设置关联成功了,我们通过overviewKey这个关键字就在array处取到了关联对象。

2. release 掉 overview(关联值)。输出结果为 “2 - associatedObject: I'm associatedObject.” 

咦?!为什么我们还能获取到关联对象呢?

这正是关联的优点之一。它可以保证关联对象在整个生命周期中都可用。即使关联值(overview)被 release 掉了,我们还能使用关联对象。
3. 移除关联,设置关联值为nil。输出结果为 “3 - associatedObject: (null)“

此时关联对象才为空。


那么,什么时候我们会用到关联呢?

比如说,在类别中添加属性。

是的,你没有听错,就是在分类中添加属性!!!

以前我们看书看视频看博客,都知道 “分类中只能添加方法,不能添加属性”。但其实这么说不准确。

不能添加属性指的是,分类不会生成setter和getter方法,所以你添加的属性不能通过self.xxx的方式调用。

事实上在分类中不能添加属性是指编译时不能添加属性,而在运行时是可以动态添加的


我们再来举个栗子

我们定义了一个NSObject的分类叫CategoryWithProperty,在这个分类中我们添加了一个属性叫property。

正常情况下我们写setter方法和getter方法应该是这样的:

[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @interface NSObject (CategoryWithProperty)  
  2. @property (nonatomicstrongNSObject *property;  
  3. @end  
  4.   
  5. @implementation NSObject (CategoryWithProperty)  
  6. - (void)setProperty:(NSObject *)value {  
  7.     _property = value;  
  8. }  
  9. - (NSObject *)property {  
  10.     return _property;  
  11. }  
  12. @end  


_property是与property属性对应的成员变量,但是在分类中是没有这个成员变量的。所以我们只能用关联来解决它:
[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @interface NSObject (CategoryWithProperty)  
  2. @property (nonatomicstrongNSObject *property;  
  3. @end  
  4.   
  5. @implementation NSObject (CategoryWithProperty)  
  6. - (void)setProperty:(NSObject *)value {  
  7.     objc_setAssociatedObject(self@selector(property), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);  
  8. }  
  9. - (NSObject *)property {  
  10.     return objc_getAssociatedObject(self@selector(property));  
  11. }  
  12. @end  


两段对照着来看会好理解一些。

setter方法无非就是把参数value的值赋给property,这不正好是objc_setAssociatedObject做的事吗?

同理,getter方法无非就是获取property的值,这不也正好是objc_getAssociatedObject做的事吗?!


我们在setter方法中把value这个值通过一个key关联到self这个源对象上,然后在getter方法中通过这个同样的key在源对象self中获取到这个值。一切都是如此的美妙。


也许你会对这个key有疑惑。(没有疑惑才不正常好吧?)

是的,通常情况下key推荐使用static char类型。但是在setter和getter这里有一个更简单的做法,那就是直接使用选择器(selector)。

因为SEL生成的时候就是一个唯一的常量,所以可以用_cmd作为objc_setAssociatedObject()的key。


我们来思考几个问题:

1.关联对象被存储在什么地方,是不是存放在被关联对象本身的内存中?

答:关联对象与被关联对象本身的存储并没有直接的关系,它是存储在单独的哈希表中的。

2.关联对象的生命周期是怎样的,什么时候被释放,什么时候被移除?

答:关联对象的释放时机与移除时机并不总是一致。

关于这些问题,可以参考点击打开链接


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值