#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,以表示由用户自己实现,不需要自动合成,然后通过关联对象来实现该属性的存取方法。如下例所示:
@interface NSObject (AssociatedObject)
@property (nonatomic, strong) id associatedObject; //category中的属性
@end
@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.
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);
}
UIImageView
category,它的imageResponseSerializer
方法允许图片通过一个滤镜来显示、或在缓存到硬盘之前改变图片的内容。cellForRowAtIndexPath:
时存储一个指向view的 UITableViewCell
中accessory view的引用,用于在 tableView:accessoryButtonTappedForRowWithIndexPath:
中使用。比起其他解决问题的方法,关联对象应该被视为最后的选择(事实上category也不应该作为首选方法)。
和其他精巧的trick、hack、workaround一样,一般人都会在刚学习完之后乐于寻找场景去使用一下。尽你所能去理解和欣赏它在正确使用时它所发挥的作用,同时当你选择这个解决办法时,也要避免当被轻蔑地问起“这是个什么玩意?”时的尴尬。
Associated Objects(关联对象),布布扣,bubuko.com
原文:http://www.cnblogs.com/lovelanjuan/p/3842174.html