运行时之关联对象

头文件:

#import <objc/runtime.h>

Objective-C 开发者习惯于警惕运行时的东西,理由是运行时改变了运行在它上面代码的实际结构。

另一方面,<objc/runtime.h> 的功能就是为应用或框架增加更强大的新特性,是其他的方式无法

实现的。同时它也可能破坏原来代码的逻辑结构,一切与之可能进行的交互,都将有可怕的副作用。

给我们带来极大的惶恐,因此,我们称之为浮士德,也是NSHipster读者经常被所要求的科目之一:

关联的对象。关联的对象或关联的引用,他们原本是Objective-C的2.0运行时的新特性,在OS
X

雪豹中介绍(可在iOS 4的)的功能。它在运行时键值允许对象为任意值,这个条目是在

<objc/
runtime.h>声明的,有以下三个C函数:

  • objc_setAssociatedObject
  • objc_getAssociatedObject
  • objc_removeAssociatedObjects

为什么会这样有用吗?它允许开发人员在分类中自定义属性,拓展的类,弥补Objective-C的这个缺陷

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));
}

人们通常的建议是使用静态字符,或者更好的是指针。这基本上保证一个任意的值是固定的、唯一的,

而且仅限于getter和setter方法??中使用:

static char kAssociatedObjectKey;

objc_getAssociatedObject(self, &kAssociatedObjectKey);

然而,一个更简单的解决方案存在:只使用一个SEL。

关联对象的行为

值可以根据由枚举类型objc_AssociationPolicy定义的行为相关联的物体上

Behavior @property Equivalent Description
OBJC_ASSOCIATION_ASSIGN @property (assign) or @property (unsafe_unretained) Specifies a weak reference to the associated object.
OBJC_ASSOCIATION_RETAIN_NONATOMIC @property (nonatomic, strong) Specifies a strong reference to the associated object, and that the association is not made atomically.
OBJC_ASSOCIATION_COPY_NONATOMIC @property (nonatomic, copy) Specifies that the associated object is copied, and that the association is not made atomically.
OBJC_ASSOCIATION_RETAIN @property (atomic, strong) Specifies a strong reference to the associated object, and that the association is made atomically.
OBJC_ASSOCIATION_COPY @property (atomic, copy) Specifies that the associated object is copied, and that the association is made atomically.

弱引用与OBJC_ASSOCIATION_ASSIGN是有区别的,弱引用和unsafe_unretained行为类似,这

意味着应该谨慎地实现访问弱关联的对象。根据在WWDC2011会话322(?36:00)的Deallocation

Timeline 描述,相关联对象的删除迟于该对象的生命周期,在object_dispose(),它是由NSObject

的-dealloc调用。

移除值

有人可能控制不住地各种诱惑,在他们某些关联的对象时调用objc_removeAssociatedObjects()。

但是,如文档中描述的,你基本没有将有机会亲自调用它:
此函数的主要目的是可以很容易将对象返回

到“原始状态”,你不应该使用这个函数从对象一般移除关联,因为它也消除了其他对象可能已添加到该

对象关联。通常情况下,你应该使用objc_setAssociatedObject 设置nil,以清除关联。

模式

  • 添加私有变量,以方便实现细节。当延伸的内置类的行为时,记录附加状态可能是必要的。这是可以当做教科书的关联对象的例子。例如,AFNetworking在UIImageView分类中使用关联对象来存储请求操作的对象,用于异步获取一个远程、特定的URL上的图像。
  • 添加公共属性来配置类的行为。有时,用属性可以使分类更加灵活,比方法参数也更有意义。在这种情况下,一个面向大众的属性是使用关联的对象是可以接受的。回到AFNetworking例子中,它的UIImageView分类的imageResponseSerializer,允许图像视图有选择地应用过滤
    器,另一方面也可以在设置和缓存到磁盘前更改远程图像的渲染。
  • 创建一个KVO的关联观察者。当在分类的实现中使用KVO时,通常推荐使用自定义的关联对象可以作为一个观察者,而不是观察的对象本身。

反模式

  • 不需要值时存储关联的对象。一个常见的视图模式是创建填充基于模型对象或复合值的字段和属性的便捷方法。如果该值以后不需要回调,就不与该对象相关联,这是可以接受的,也是合理的。
  • 当 值可以被推断出来时存储关联的对象。例如,一个人也许会存储一个自定义辅助视图的包含的UITableViewCell的引用,用于实现代码如 下:tableView:accessoryButtonTappedForRowWithIndexPath:,这时候可以通过调用
    cellForRowAtIndexPath: 恢复。
  • 在如下的情况使用关联的对象:
    • 继承比组合更合理时的子类。
    • 添加交互事件来响应的 "目标-动作"
    • 目标-动作不起作用时的 手势识别。
    • 行为可以委托给另一个对象的代理。
    • 松耦合方式的跨系统通信事件的通知、通知中心。

相关联的对象应被看作是不得已的方法,而不是寻找问题的解决方案(说真的,类本身真的不应该在工具链的顶端开始)。 如同任何聪明的把戏,骇客攻击,或解决方法,一般都会积极寻求应用的场合,尤其是了解之后,这是人的一种自然的倾向
。尽你所能地理解和欣赏时,这是正确的解决方案,保存自己被轻蔑地问:“为什么要以神的名义”,然后你决定去解决。

相关代码:

https://github.com/skyming/BlockUI/tree/master/BlockUI

原文连接:

http://nshipster.com/associated-objects/

时间: 2024-10-29 19:10:37

运行时之关联对象的相关文章

Javascript 笔记与总结(2-9)获取运行时的 style 对象

获取内存中(正在渲染)的 style 的值(非内联 style,obj.style 只能获得内联 style 的值),可以用 obj.currentStyle(低版本 IE 和 Opera 支持)和 window.getComputedStyle(IE9 以及 标准浏览器支持)来获取. window.getComputedStyle 的格式是 window.getComputedStyle(obj,伪元素) 第一个参数是要要获取计算后的样式的目标元素 第二个参数是期望的伪元素,如"after&q

创建运行时类的对象,调用指定的属性方法构造器

1 使用newInstance(),实际上是调用运行时空参的构造器    注意构造器的权限修饰符要足够,同时必须有一个空参的构造器. 2 调用指定的属性方法构造器 package lianxi1; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Meth

java反射系列四之创建运行时类的对象

一.概念认知 二.代码示例 package reflect; public class TestConstructor { public static void main(String[] args) throws Exception { TestConstructor T = new TestConstructor(); T.test(); } public void test() throws Exception{ String className = "reflect.Person&quo

通过运行时动态给分类添加属性

#import <UIKit/UIKit.h> /** iOS 开发中,分类默认不允许保存属性 如果在分类中,定义一个属性,需要自己实现 getter & setter 方法,而且没有 _成员变量 如果在自己开发的框架中,希望在分类中动态添加属性,可以通过 OC 运行时的关联对象的功能! 运行时非常重要的一个应用:给分类动态添加属性,结果能够让框架包装的更好,让使用者做出最小的修改! */ @interface UIImageView (WebImage) @property (non

Objective-C Runtime 运行时之一:类与对象

Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理.这种动态语言的优势在于:我们写代码时能够更具灵活性,如我们可以把消息转发给我们想要的对象,或者随意交换一个方法的实现等. 这种特性意味着Objective-C不仅需要一个编译器,还需要一个运行时系统来执行编译的代码.对于Objective-C来说,这个运行时系统就像一个操作系统一样:它让所有的工作可以正常的运行.这个运行时系统即Objc Runtime.Objc Runtime其实是一个Runti

[ObjectC]Runtime 运行时之一:类与对象

Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理.这种动态语言的优势在于:我们写代码时更具灵活性,如我们可以把消息转发给我们想要的对象,或者随意交换一个方法的实现等. 这种特性意味着Objective-C不仅需要一个编译器,还需要一个运行时系统来执行编译的代码.对于Objective-C来说,这个运行时系统就像一个操作系统一样:它让所有的工作可以正常的运行.这个运行时系统即Objc Runtime.Objc Runtime其实是一个Runtime

Runtime 运行时之一:类与对象

Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理.这种动态语言的优势在于:我们写代码时能够更具灵活性,如我们可以把消息转发给我们想要的对象,或者随意交换一个方法的实现等. 这种特性意味着Objective-C不仅需要一个编译器,还需要一个运行时系统来执行编译的代码.对于Objective-C来说,这个运行时系统就像一个操作系统一样:它让所有的工作可以正常的运行.这个运行时系统即Objc Runtime.Objc Runtime其实是一个Runti

Objective-C Runtime 运行时之二:成员变量与属性

类型编码(Type Encoding) 作为对Runtime的补充,编译器将每个方法的返回值和参数类型编码为一个字符串,并将其与方法的selector关联在一起.这种编码方案在其它情况下也是非常有用的,因此我们可以使用@encode编译器指令来获取它.当给定一个类型时,@encode返回这个类型的字符串编码.这些类型可以是诸如int.指针这样的基本类型,也可以是结构体.类等类型.事实上,任何可以作为sizeof()操作参数的类型都可以用于@encode(). 在Objective-C Runti

iOS学习之Objective-C 2.0 运行时系统编程

0 导言 本主主要内容包括: 1.概述2.参考3.运行时系统的版本和平台4.和运行时系统的交互5.消息6.动态方法解析7.消息转发8.类型编码9.属性声明 1 概述 Objective-C语言将决定尽可能的从编译和链接时推迟到运行时.只要有可能,Objective-C总是使用动态的方式来解决问题.这意味着Objective-C语言不仅需要一个编译器,同时也需要一个运行时系统来执行编译好的代码.这里的运行时系统扮演的角色类似于 Objective-C语言的操作系统,Objective-C基于该系统