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


69

在我进行的一个项目中,原始作者选择使用它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);

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

Answers:


34

从《Objective-C运行时参考》的参考文档中:

您可以使用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点),将释放概述,因此在这种情况下也将释放。例如,如果尝试记录概述的值,则会生成运行时异常。


嗨,我正在审查文档,目的是发布类似的答案。当您击败我时,我自由地在您的答案中建立了超文本链接。希望你不要介意。
JeremyP,2011年

@JeremyP:好的。为此非常感谢。错过添加链接。感谢您的贡献
Visakh7 2011年

如果可以的话,可以分享一些声誉。:P
visakh7

1
这就是我要问的...如果已经是ivar,为什么还要这么做呢?(老实说,我没有写这段代码。)
Jasarien

3
相对于现有的保留/释放/自动释放等,尚不清楚在哪些情况下将是有用/需要的...
Nicolas Miari 2012年

64

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

如果要在主实现之外存储属于某个对象的东西,这真的很方便。主要用例之一是无法添加实例变量的类别。在这里,您objc_setAssociatedObject可以将其他变量附加到self对象。

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


感谢您的解释。阅读本文后,我怀疑代码是否正在以有用的方式使用该函数。我编辑了问题以包含代码。
2011年

在这种情况下,我们不能使用字典保留对象吗?
AppleBee 2015年

@ user3127620(就是objc_setAssociatedObject这样)。这种机制的优点在于,它无需更改类即可完成此操作(即,无需添加ivar)。因此,您可以在框架类或其他您不拥有源代码的情况下使用它。
Nikolai Ruhe 2015年

您好@NikolaiRuhe-您可能知道此问题的完整答案/解释:stackoverflow.com/q/41827988/294884 Troky :/
Fattie

27

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

一:将实例变量添加到类别中。通常,建议不要使用此技术,但这是合法使用的示例。假设您要模拟无法修改的对象的其他实例变量(我们正在谈论修改对象本身,即不带子类化)。假设在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

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


二:动态添加状态信息未包括在与KVO结合它的实例变量的对象。

这个想法是您的对象仅在运行时(即动态)获得状态信息。因此,想法是,尽管您可以将此状态信息存储在实例变量中,但实际上您是将此信息附加到在运行时实例化的对象中并与其他对象动态关联的事实,但您仍在强调以下事实:对象的动态状态。

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

优点看看AFNetworking库的开创者Mattt Thompson对相关对象的讨论/解释


完善!正是我需要的!
伦纳德·保利

答案中的原因一和原因二相同。它们都通过类别将实例变量添加到对象。
Jasarien

@Jasarien,我不好。
2013年

这确实是转储,编译器应该只允许类别中的综合属性。
malhal 2013年

很抱歉,但是我不太明白为什么在这里提到KVO。因为它似乎与associatedObject没有任何关系...
Calios

5

要回答您的修订问题:

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

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

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

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


这次你击败了我:P。
visakh7 2011年

2
我想同意你的观点。它闻起来。我完全打算正确地做到这一点。感谢您的澄清。
Jasarien
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.