什么是objc_setAssociatedObject()以及在什么情况下应该使用它?

在我接受的一个项目中,原作者select使用objc_setAssociatedObject() ,我不能100%清楚它做了什么或者他们为什么决定使用它。

我决定查阅,不幸的是,这些文件对其目的不是很明确。

objc_setAssociatedObject
使用给定的键和关联策略设置给定对象的关联值。
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)
参数
object
关联的源对象。
key
协会的关键。
value
与对象的键关联的值。 通过零来清除现有的关联。
policy
协会的政策。 有关可能的值,请参阅“关联对象行为”。

那么这个函数究竟做了什么,在什么情况下应该使用它呢?


阅读答案后编辑

那么下面的代码有什么意义呢?

 Device *device = [self.list objectAtIndex:[indexPath row]]; DeviceViewController *next = [[DeviceViewController alloc] initWithController:self.controller device:device item:self.rootVC.selectedItem]; objc_setAssociatedObject(device, &kDeviceControllerKey, next, OBJC_ASSOCIATION_RETAIN); 

如果它已经是一个实例variables,那么将设​​备与视图控制器关联起来有什么意义呢?

从Objective-C运行时引用的参考文档:

您使用Objective-C运行时函数objc_setAssociatedObject在一个对象与另一个对象之间build立关联。 该函数有四个参数:源对象,键,值和关联策略常量。 关键是一个空指针。

  • 每个关联的关键必须是唯一的。 一个典型的模式是使用一个静态variables。
  • 该策略指定是否分配关联的对象,
    保留,还是复制,以及是否
    协会是primefaces或做
    非primefaces。 这种模式是
    类似于属性的
    一个声明的属性(请参阅“属性”
    声明属性“)。 您可以使用常数指定关系的策略(请参阅
    objc_AssociationPolicy和
    关联对象行为)。

在数组和string之间build立关联

 static char overviewKey; NSArray *array = [[NSArray alloc] initWithObjects:@"One", @"Two", @"Three", nil]; // For the purposes of illustration, use initWithFormat: to ensure // the string can be deallocated NSString *overview = [[NSString alloc] initWithFormat:@"%@", @"First three numbers"]; objc_setAssociatedObject ( array, &overviewKey, overview, OBJC_ASSOCIATION_RETAIN ); [overview release]; // (1) overview valid [array release]; // (2) overview invalid 

在点1,string总览仍然有效,因为OBJC_ASSOCIATION_RETAIN策略指定数组保留关联的对象。 然而,当数组被释放时(在点2),概要被释放,因此在这种情况下也被释放。 例如,如果尝试logging概述的值,则会生成运行时exception。

objc_setAssociatedObject为每个Objective-C对象添加一个键值存储。 它允许您为对象存储额外的状态,而不是反映在其实例variables中。

当您想要存储属于主实现之外的对象的东西时,这非常方便。 其中一个主要的用例是在你不能添加实例variables的类别中。 这里使用objc_setAssociatedObject将附加variables附加到self对象上。

当使用正确的关联策略时,当主对象被释放时,对象将被释放。

这里是对象关联的用例列表:

一:将实例variables添加到类别。 一般来说,这种技术是被禁止的,但这是一个合法使用的例子 。 假设你想为不能修改的对象模拟额外的实例variables(我们正在讨论修改对象本身,即没有子类化)。 假设在UIImage上设置标题。

 // UIImage-Title.h: @interface UIImage(Title) @property(nonatomic, copy) NSString *title; @end // UIImage-Title.m: #import <Foundation/Foundation.h> #import <objc/runtime.h> static char titleKey; @implementation UIImage(Title) - (NSString *)title { return objc_getAssociatedObject(self, &titleKey); } - (void)setTitle:(NSString *)title { objc_setAssociatedObject(self, &titleKey, title, OBJC_ASSOCIATION_COPY); } @end 

此外, 这是一个相当复杂(但真棒)的方式使用关联的对象类别..它基本上允许你传递一个块,而不是select器到UIControl


二:dynamic地将状态信息添加到与KVO结合的实例variables未涵盖的对象。

这个想法是,你的对象只在运行时(即dynamic)获得状态信息。 所以这个想法是,尽pipe你可以把这个状态信息存储在一个实例variables中,但是你将这个信息附加到一个在运行时实例化的对象上,并且dynamic地将它与另一个对象关联起来,你可以强调这一点对象的dynamic状态。

一个很好的例子就是这个库,其中关联对象与KVO通知一起使用。 下面是代码的摘录(注意:这个KVO通知不是必须运行的,使该库中的代码工作..而是为了方便起见作者放在那里,基本上任何注册到此的对象都将通过KVO发生了变化):

 static char BOOLRevealing; - (BOOL)isRevealing { return [(NSNumber*)objc_getAssociatedObject(self, &BOOLRevealing) boolValue]; } - (void)_setRevealing:(BOOL)revealing { [self willChangeValueForKey:@"isRevealing"]; objc_setAssociatedObject(self, &BOOLRevealing, [NSNumber numberWithBool:revealing], OBJC_ASSOCIATION_RETAIN_NONATOMIC); [self didChangeValueForKey:@"isRevealing"]; } 

奖金:看看这个关于对象的讨论/解释 ,Mattt Thompson是AFNetworking图书馆的创始人

要回答您的修改问题:

如果它已经是一个实例variables,那么将设​​备和视图控制器关联起来有什么意义呢?

有几个原因可能会导致这种情况。

  • 设备类没有控制器实例variables或属性,你不能改变它或子类,例如你没有源代码。
  • 你想要两个控制器与设备对象关联,你不能改变设备类或子类。

就个人而言,我认为很less需要使用低级别的Objective-C运行时function。 这看起来像一个代码味道给我。