本文翻译自NSHipster的文章Associated Objects。
#import <objc/runtime.h>
Objective-C开发者在遇到上面这条“咒语”相关的一些东西时,会不自觉的变的非常谨慎。一个主要原因是:弄乱Objective-C运行时可能会改变整个实现结构,因为所有的代码都是运行在它之上的。
一方面:<objc/runtime.h>
中的函数可以给应用或者框架增加强大的新特性,这是通过其他方式不可能做到的。但另一方面:它会改变代码的正常运行逻辑和所有与之交互的东西(通常伴随着可怕的副作用)。
因而,这是我们认为进行这种魔鬼交易最大的恐惧点,下面来看一个NSHipster读者问得最多的一个主题:associated objects。
Associated Objects(关联对象)或者叫作关联引用(Associative References),是作为Objective-C 2.0 运行时功能被引入到 Mac OS X 10.6 Snow Leopard(及iOS4)系统。与它相关在<objc/runtime.h>
中有3个C函数,它们可以让对象在运行时关联任何值:
objc_setAssociatedObject
objc_getAssociatedObject
objc_removeAssociatedObjects
为什么这几个方法很有用呢?因为开发者可以通过它们在分类中给已存在的类中添加自定义属性。
NSObject+AssociatedObject.h
@interface NSObject (AssociatedObject)
@property (nonatomic, strong) id associatedObject;
@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));
}
通常推荐key使用static char
类型——使用指针或许更好,key值是一个唯一的常量,并只在getters和setters方法内部使用:
static char kAssociatedObjectKey;
objc_getAssociatedObject(self, &kAssociatedObjectKey);
然而,一个更简单的方案是:直接使用选择器(selector)。
因为SEL生成的时候就是一个唯一的常量,你可以使用 _cmd 作为objc_setAssociatedObject()的key。
—— Bill Bumgarner(@bbum) August28, 2009
关联对象的特性
被关联到对象的值根据使用的objc_AssociationPolicy
类型不同表现出不同的特性:
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
分配的弱关联对象并不是完全和weak
修饰符引用一样(对象初始化与释放时被置空),反而更像是unsafe_unretained
,所以你需要在访问弱关联对象时稍微注意一下。
根据WWDC2011,Session322对对象释放时间的描述,associated objects清除在对象生命周期中很晚才执行,通过被
NSObject -dealloc
方法调用的object_dispose()
函数完成。
移除关联对象
一个的方法是试图在某个时刻调用objc_removeAssociatedObjects()
函数来移除关联对象,然而,根据苹果文档描述,你不大可能有需求要自己去调用:
这个函数的主要目的是很容易的让对象恢复成它“原始状态”,你不应该使用它来移除关联的对象,因为它也会移除掉包括其他地方加入的全部的关联对象。所以一般你只需要通过调用
objc_setAssociatedObject
并传入nil值来清除关联值。
模式
-
添加私有变量来帮助实现细节 。当拓展一个内置类时,可能有必要跟踪一些额外的状态,这是关联对象最普遍的应用场景。例如:AFNetworking中在
UIImageView
的分类中使用关联对象来存储一个请求操作对象(operation object),用于异步的从远程获取图片。 -
添加公共属性来设置分类的特性 。有时候,通过添加一个属性让一个分类更加灵活,而不是作为函数参数。这种情况下,使用关联对象作为一个公开的属性是可接受的解决方案。还是拿前面AFNetworking的例子来说,
UIImageView
的分类中imageResponseSerializer
属性允许图片视图随意的使用一个过滤器,或者在图片请求并缓存之前就可以修改它的渲染。 -
为KVO创建一个关联的观察者(observer)。当在一个分类中使用KVO的时候,推荐使用一个自定义的关联对象作为观察者,而不是对象自己观察自己。
反模式
-
在不必要的时候使用关联对象。使用视图时一个常见的情况是通过数据模型或一些复合的值来创建一个便利的方法设置填充字段或属性。如果这些值在后面不会再被使用到,最好就不要使用关联对象了。
-
使用关联对象来保存一个可以被推算出来的值。例如,有人可能想通过关联对象存储
UITableViewCell
上一个自定义accessoryView的引用,使用tableView:accessoryButtonTappedForRowWithIndexPath:
和cellForRowAtIndexPath:
即可以达到要求。 -
使用关联对象来代替X。其中X代表下面的一些项:
- 子类化,当使用继承比使用组合更合适的时候。
- Target-Action给响应者添加交互事件。
- 手势识别,当target-action模式不够用的时候。
- 代理,当事件可以委托给其他对象。
- 消息 & 消息中心使用低耦合的方式来广播消息。
关联对象应该被当做最后的手段来使用(不得不用时才用),而不是为了寻求一个解决方案就行(事实上,分类本身就不应该是解决问题优先选择的工具)。
像一些巧妙的伎俩、hack手段或者是变通的解决方案,人们总是倾向于创造机会来使用他们——特别是刚刚接触他们时。尽可能的在理解并领悟之后再做出正确的方案,避免自己陷入一知半解的尴尬处境。
thx: http://esoftmobile.com/2014/02/18/associated-objects/
相关推荐
Objective-C中的Associated Objects机制允许我们给已有的类动态地添加属性,即使这个类的源代码不可访问,这对于很多开发者而言是一个非常实用的功能。这项技术在使用时涉及三个关键的函数:objc_...
Swift中的类型安全关联对象(Type-Safe Associated Objects)是一种用于扩展类、结构体或枚举类型功能的方法,它允许我们在不使用子类化的情况下为类型添加关联的存储属性。这个开源项目"swift-tsao"旨在提供一个...
3. **Associated Objects**:Objective-C的Runtime提供了一种关联对象的机制,允许我们在运行时将数据关联到任何对象,而无需继承或使用分类。 4. **KVC(Key-Value Coding)与KVO(Key-Value Observing)**:KVC...
5. Category与 Associated Objects:了解Category如何扩展已有类的功能,以及Associated Objects如何实现类的私有属性。 通过本Demo,你可以深入学习和实践Runtime,提升你的iOS开发技能。在实践中不断探索,...
- ** Associated Objects:**一种非正式的协议,允许在运行时将任意对象关联到任何其他对象。 通过深入研究Objc4--750中的runtime源码,开发者不仅可以更好地理解Objective-C的底层机制,还能掌握如何利用这些机制...
Associated Objects是Objective-C运行时提供的一种机制,让我们可以在分类中为类添加属性。首先,我们需要导入`objc/runtime.h`头文件。然后,使用`objc_setAssociatedObject`和`objc_getAssociatedObject`方法来...
2. **Associated Objects**: 上述Category中,我们使用了关联对象(Associated Objects)来存储属性的值。这是Runtime提供的一种机制,可以将任意对象关联到另一个对象上,用于扩展非自身实例变量的属性。`objc_...
此外,如果源码中涉及到Category与分类冲突的问题,可能会看到解决category命名空间冲突的策略,比如使用前缀或者使用 Associated Objects 来存储分类中的属性。 总之,"IOS应用源码——分类.zip"提供了学习和实践...
- **Working the Association**: This covers how to manipulate associated objects and ensure that changes are correctly synchronized with the database. - **Collection of Values**: This introduces the ...
如果需要存储数据,可以使用 Associated Objects 或 Category 的分类扩展(Extension)。 接下来,我们讨论 Protocol。Protocol 在 Objective-C 中相当于其他语言的接口,定义了一组方法签名,对象可以选择遵循这些...
还有可能涉及分类(Categories)和关联对象(Associated Objects),这些都是Objective-C中的扩展机制。 3. **从c++到Objective-C.pdf** 这本书针对已经熟悉C++的开发者,旨在帮助他们过渡到Objective-C。C++程序员...
但有一种方式可以实现为类别动态添加属性,这通常通过关联对象(Associated Objects)来实现。 关联对象是Objective-C运行时提供的一种机制,它允许我们在运行时为任何对象添加属性,无论这个对象是否有继承体系。...
8. ** Associated Objects**: 通过Runtime,我们可以将数据关联到任何对象上,即使这个对象本身没有对应的属性。这是通过`objc_setAssociatedObject()`和`objc_getAssociatedObject()`函数实现的,常用于扩展已有...
另外,Runtime还可以帮助开发者实现自定义的 setter 和 getter,控制属性的读写行为,以及进行性能优化,比如利用关联对象(Associated Objects)来存储非正式协议或者附加数据。 文章中还提到了其他iOS开发相关的...
然而,可以使用 Associated Objects 技术来模拟添加属性。 3. **类别与协议**:分类可以遵循协议,这样就可以使原有的类符合该协议,从而支持更多的功能或数据交换。 4. **分类的初始化与加载**:分类的加载发生在...
上述代码使用了Runtime的关联对象(Associated Objects)来为`UITextField`添加一个自定义的setter方法,然后通过KVC找到`_placeholderLabel`子视图并设置其颜色。注意,`_placeholderLabel`是`UITextField`的私有...
如果需要存储数据,可以考虑使用关联对象(Associated Objects)或者分类中的静态变量。关联对象是Objective-C runtime提供的一种机制,可以通过key-value的方式将数据关联到任何对象上。 分类有时会导致命名冲突,...
7. **Protocol & Delegate with Associated Objects(关联对象)** 在某些情况下,代理可能需要保留到对象生命周期之外,这时可以使用关联对象。通过运行时添加关联对象,即使在代理对象销毁后,仍然可以保持通信...
3. **对象关联(Associated Objects)**:Category中添加的属性,如`UIViewController`的`urlAction`,通常通过对象关联实现。这是一种在运行时将数据与任何对象关联起来的方法,即使该类本身并未声明该属性。`objc_...
由于Category不能直接添加实例变量,我们需要使用关联对象(Associated Objects)来实现。通过`objc_setAssociatedObject`和`objc_getAssociatedObject`这两个API,我们可以将数据关联到Category上,从而实现属性的...