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

时间:2011-05-06 09:28:32

标签: iphone objective-c objective-c-runtime

在我接受的一个项目中,原作者选择使用objc_setAssociatedObject()并且我不是100%明确它的作用或者他们决定使用它的原因。

我决定查阅一下,不幸的是,文档对其目的并不十分具有描述性。

objc_setAssociatedObject
使用给定的键和关联策略为给定对象设置关联值 void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)
参数
object
关联的源对象 key
协会的关键。
value
与对象的键键关联的值。通过nil清除现有的关联。
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);

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

4 个答案:

答案 0 :(得分:58)

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

当您想要存储属于主要实现之外的对象的东西时,这非常方便。其中一个主要用例是您无法添加实例变量的类别。在这里,您可以使用objc_setAssociatedObject将其他变量附加到self对象。

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

答案 1 :(得分:32)

来自Objective-C Runtime Reference上的参考文件:

  

您使用Objective-C运行时   函数objc_setAssociatedObject来   在一个对象之间建立关联   和另一个。该功能需要四个   参数:源对象,一键,   价值和关联政策   不变。关键是无效指针。

     
      
  • 每个关联的关键字必须是唯一的。典型的模式是   使用静态变量。
  •   
  • 该策略指定是否分配了关联对象,
      保留,或复制,以及是否   关联是以原子方式或以   非原子。这种模式是
      类似于属性的   声明的财产(见“财产”   声明属性“)。你指定   关系使用的政策   常数(见
      objc_AssociationPolicy和
      关联对象行为)。
  •   

在数组和字符串之间建立关联

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点,字符串概述是   仍然有效,因为   OBJC_ASSOCIATION_RETAIN政策   指定数组保留   相关对象。当数组是   然而(在第2点),已经解除分配   概述已发布,因此在此   案件也被解除分配。如果你试过,   例如,记录值   概述,您生成一个运行时   异常。

答案 2 :(得分:25)

以下是对象关联的用例列表:

one:将实例变量添加到类别。一般来说,这种技术是advised反对,但这是合法使用的example。让我们假设你想为你无法修改的对象模拟其他实例变量(我们正在讨论修改对象本身,即没有子类化)。让我们说在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

此外,here是一种非常复杂(但很棒)的方法,可以将关联对象与类别一起使用..它基本上允许您将块而不是选择器传递给UIControl


二:将状态信息动态添加到与KVO结合的实例变量未涵盖的对象中。

我们的想法是,您的对象仅在运行时(即动态)获取状态信息。因此,我们的想法是,尽管您可以将此状态信息存储在实例变量中,但事实上您将此信息附加到在运行时实例化的对象并将其与其他对象动态关联,您突出显示的事实是:这是对象的动态状态。

说明这一点的一个很好的例子是this库,其中关联对象与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"];
}

奖金:看看这个discussion/explanation相关对象,由开创性AFNetworking图书馆的作者Mattt Thompson撰写

答案 3 :(得分:5)

回答您修改过的问题:

  

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

您可能希望这样做有几个原因。

  • Device类没有控制器实例变量或属性,您无法更改它或将其子类化,例如你没有源代码。
  • 您需要两个与设备对象关联的控制器,并且您无法更改设备类或将其子类化。

就我个人而言,我认为很少需要使用低级Objective-C运行时函数。这看起来像是一种代码味道。