Associated Objects(关联对象)

#import <objc/runtime.h>

Objective-C开发者应该小心谨慎地遵循这个危险咒语的各种准则。一个很好的原因的就是:混乱的运行时代码会改变运行在其架构之上的所有代码。

从利的角度来讲, <objc/runtime.h> 中的函数具有其他方式做不到的、能为应用和框架提供强大功能的能力。而从弊的角度来讲,它可能会会毁掉代码的sanity meter,一切代码和逻辑都可能被异常糟糕的副作用影响(terrifying side-effects)。

因此,我们怀着巨大的恐惧来思考这个与“魔鬼的交易”(Faustian bargain),一起来看看这个最多地被NSHipster读者们要求讲讲的主题之一:对象关联(associated objects)。



对象关联(或称为关联引用)本来是Objective-C 2.0运行时的一个特性,起始于Mac OS X 10.6 Snow Leopard和iOS 4。相关参考可以查看 <objc/runtime.h> 中定义的以下三个允许你将任何键值在运行时关联到对象上的函数:

  • objc_setAssociatedObject : void objc_setAssociatedObject(id object, void*key, id value,objc_AssociationPolicy policy)
  • objc_getAssociatedObject : void objc_getAssociatedObject(id object, void*key)
  • objc_removeAssociatedObjects : void objc_removeAssociatedObjects(id object)

为什么我说这个很有用呢?因为这允许开发者对已经存在的类在扩展(category)中添加自定义的属性,这几乎弥补了Objective-C最大的缺点

PS:在《Effective objective-c 2.0》中第26条中作者谈到了 勿在类的扩展(category)中声明属性。因为类的扩展目的在于扩展功能,而非封装数据。在category扩展类中声明的属性,此扩展类无法合成与该属性的存取方法。此时可以把该属性声明为@dynamic,以表示由用户自己实现,不需要自动合成,然后通过关联对象来实现该属性的存取方法。如下例所示:

NSObject+AssociatedObject.h

@interface NSObject (AssociatedObject)
@property (nonatomic, strong) id associatedObject; //category中的属性
@end

NSObject+AssociatedObject.m

@implementation NSObject (AssociatedObject)
@dynamic associatedObject;

- (void)setAssociatedObject:(id)object {
     objc_setAssociatedObject(self, @selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (id)associatedObject {
    return objc_getAssociatedObject(self, @selector(associatedObject));
}

通常推荐的做法是添加的属性最好是 static char 类型的,当然更推荐是指针型的(static const char *kNsobjectPropertyKey = "kNsobjectPropertyKey")。通常来说该属性应该是常量、唯一的、在适用范围内用getter和setter访问到:

static char kAssociatedObjectKey;

objc_getAssociatedObject(self, &kAssociatedObjectKey);

然而可以用更简单的方式实现:用selector。

Since SELs are guaranteed to be unique and constant, you can use _cmd as the key forobjc_setAssociatedObject()#objective-c #snowleopard   _cmd代表当前方法的SEL指针,可以通过NSStringFromSelector(_cmd)方法获取该方法的名字。

— Bill Bumgarner (@bbum) August 28, 2009

关联对象的行为

属性可以根据定义在枚举类型 objc_AssociationPolicy 上的行为被关联在对象上:

Behavior @property Equivalent Description
OBJC_ASSOCIATION_ASSIGN @property (assign) 或@property (unsafe_unretained) 指定一个关联对象的弱引用。
OBJC_ASSOCIATION_RETAIN_NONATOMIC @property (nonatomic, strong) 指定一个关联对象的强引用,不能被原子化使用。
OBJC_ASSOCIATION_COPY_NONATOMIC @property (nonatomic, copy) 指定一个关联对象的copy引用,不能被原子化使用。
OBJC_ASSOCIATION_RETAIN @property (atomic, strong) 指定一个关联对象的强引用,能被原子化使用。
OBJC_ASSOCIATION_COPY @property (atomic, copy) 指定一个关联对象的copy引用,能被原子化使用。

以 OBJC_ASSOCIATION_ASSIGN 类型关联在对象上的弱引用不代表0 retian的 weak 弱引用,行为上更像 unsafe_unretained 属性,所以当在你的视线中调用weak的关联对象时要相当小心。

根据WWDC 2011, Session 322 (第36分钟左右)发布的内存销毁时间表,被关联的对象在生命周期内要比对象本身释放的晚很多。它们会在被 NSObject -dealloc 调用的object_dispose() 方法中释放。

删除属性

你可以会在刚开始接触对象关联时想要尝试去调用 objc_removeAssociatedObjects() 来进行删除操作,但如文档中所述,你不应该自己手动调用这个函数:

The main purpose of this function is to make it easy to return an object to a "pristine state”. You should not use this function for general removal of associations from objects, since it also removes associations that other clients may have added to the object. Typically you should use objc_setAssociatedObject with a nil value to clear an association.

优秀样例(Patterns)

  • 添加私有属性用于更好地去实现细节。当扩展一个内建类的行为时,保持附加属性的状态可能非常必要。注意以下说的是一种非常教科书式的关联对象的用例:AFNetworking在UIImageView 的category上用了关联对象来保持一个operation对象,用于从网络上某URL异步地获取一张图片。

    • @property (readwrite, nonatomic, strong, setter = af_setImageRequestOperation:) AFHTTPRequestOperation *af_imageRequestOperation;

      - (AFHTTPRequestOperation *)af_imageRequestOperation {

      return (AFHTTPRequestOperation *)objc_getAssociatedObject(self, @selector(af_imageRequestOperation));

      }

      - (void)af_setImageRequestOperation:(AFHTTPRequestOperation *)imageRequestOperation {

      objc_setAssociatedObject(self, @selector(af_imageRequestOperation), imageRequestOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

      }

  • 添加public属性来增强category的功能。有些情况下这种(通过关联对象)让category行为更灵活的做法比在用一个带变量的方法来实现更有意义。在这些情况下,可以用关联对象实现一个一个对外开放的属性。回到上个AFNetworking的例子中的 UIImageView category,它的imageResponseSerializer方法允许图片通过一个滤镜来显示、或在缓存到硬盘之前改变图片的内容。
  • 创建一个用于KVO的关联观察者。当在一个category的实现中使用KVO时,建议用一个自定义的关联对象而不是该对象本身作观察者。When using KVO in a category implementation, it is recommended that a custom associated-object be used as an observer, rather than the object observing itself.

反例(Anti-Patterns)

  • 当值不需要的时候建立一个关联对象。一个常见的例子就是在view上创建一个方便的方法去保存来自model的属性、值或者其他混合的数据。如果那个数据在之后根本用不到,那么这种方法虽然是没什么问题的,但用关联到对象的做法并不可取。
  • 当一个值可以被其他值推算出时建立一个关联对象。例如:在调用cellForRowAtIndexPath: 时存储一个指向view的 UITableViewCell 中accessory view的引用,用于在 tableView:accessoryButtonTappedForRowWithIndexPath: 中使用。
  • 用关联对象替代X,这里的X可以代表下列含义:

比起其他解决问题的方法,关联对象应该被视为最后的选择(事实上category也不应该作为首选方法)。

和其他精巧的trick、hack、workaround一样,一般人都会在刚学习完之后乐于寻找场景去使用一下。尽你所能去理解和欣赏它在正确使用时它所发挥的作用,同时当你选择这个解决办法时,也要避免当被轻蔑地问起“这是个什么玩意?”时的尴尬。

Associated Objects(关联对象)

时间: 2024-10-03 10:03:33

Associated Objects(关联对象)的相关文章

iOS关联对象

Associated Objects(关联对象)或者叫作关联引用(Associative References),是作为Objective-C 2.0 运行时功能被引入到 Mac OS X 10.6 Snow Leopard(及iOS4)系统.与它相关在<objc/runtime.h>中有3个C函数,它们可以让对象在运行时关联任何值: OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value,

Objective-C——关联对象

动态语言 OC是一种动态语言,它的方法,对象的类型都是到运行的时候才能够确定的.所以这就使得OC存在了关联对象这一强大的机制. 关联对象 所谓关联对象,其实就是我们在运行时对一个已存在的对象上面绑定一个对象,使两个对象变成动态的聚合关系. 关联对象和属性一样有着关键字,以下是关联对象的存储策略: 关联类型 等效的@property属性 OBJC_ASSOCIATION_ASSIGN assign OBJC_ASSOCIATION_RETAIN_NONATOMIC nonatomic,retain

关联对象

一.关联对象所用的方法主要有两个: 1>设置关联对象值:objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) 2>根据Key取出关联对象的关联值:objc_getAssociatedObject(id object, const void *key) 其中: object:关联的对象, key存和取关联值的键, objc_AssociationPolicy po

iOS-runtime-objc_setAssociatedObject(关联对象以及传值)

例子: static const char kRepresentedObject; - (IBAction)doSomething:(id)sender { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Alert" message:nil delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; objc_setAssociate

hibernate关联对象的增删改查------查

本篇博客是之前博客hibernate关联对象的增删改查------查 的后继,本篇代码的设定都在前文已经写好,因此读这篇之前,请先移步上一篇博客 //代码片5 SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); session.beginTransa

iOS objc_setAssociatedObject 关联对象的学习

今天看了FDTemplateLayoutCell的源码,类别里面相当频繁使用了关联对象,做笔记!!!学套路 主要函数: void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy); id objc_getAssociatedObject(id object, const void *key); void objc_removeAssociatedObjects

关联对象和objc_msgSend

关联对象,其实就是给某个对象关联其他一些对象,这些对象通过键来设置和存储,并且存储对象值得时候可以设置存储策略,常用在类别中 比如我们给UIButton创建一个类别,不用将button处理的事件分开写了 2.消息传递 OC是C的超集,C语言使用的是静态绑定,在编译期就能决定运行时候调用的函数,而OC在给对象传递消息的时候,是利用动态绑定来决定所要调用的方法,是在运行期才知道该调用的方法,甚至可以在运行期改变方法,这使得OC是一门及其动态的语言 3.消息转发 这里涉及到一个问题就是,当某个对象接收

Runtime之成员变量&amp;属性&amp;关联对象

上篇介绍了Runtime类和对象的相关知识点,在4.5和4.6小节,也介绍了成员变量和属性的一些方法应用.本篇将讨论实现细节的相关内容. 在讨论之前,我们先来介绍一个很冷僻但又很有用的一个关键字:@encode 1.类型编码 为了协助运行时系统,编译器用字符串为每个方法的返回值.参数类型和方法选择器编码,使用的编码方案在其他情况下也很有用.在 Objective-C 运行时的消息发送机制中,传递参数时,由于类型信息的缺失,需要类型编码进行辅助以保证类型信息也能够被传递.在实际的应用开发中,使用案

mybatis中的关联对象查询

方式1(嵌套查询): 在本类的mapper映射配置文件中的ResultMap标签中使用association子标签,对关联对象的属性进行关联 例如:User中关联Department(多对一) ----------User的mapper映射配置文件---------<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Map