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

Jas*_*ien 67 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);
Run Code Online (Sandbox Code Playgroud)

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

Nik*_*uhe 58

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

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

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


vis*_*kh7 33

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

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

  • 每个关联的关键必须是唯一的.典型的模式是使用静态变量.
  • 策略指定是否分配,
    保留或复制关联对象,以及
    是以原子方式还是
    非原子方式建立关联.此模式
    类似于
    声明属性的属性(请参阅"属性
    声明属性").您可以使用常量指定关系的策略(请参阅
    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
Run Code Online (Sandbox Code Playgroud)

在第1点,字符串概述仍然有效,因为OBJC_ASSOCIATION_RETAIN策略指定数组保留关联的对象.然而,当数组被释放时(在第2点),概述被释放,因此在这种情况下也被解除分配.例如,如果您尝试记录overview的值,则会生成运行时异常.

  • 仍然不清楚在哪种情况下这将是有用/需要的,而不是现有的保留/释放/自动释放等... (3认同)

abb*_*ood 25

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

一:将实例变量添加到类别.一般来说,建议使用此技术,但这是一个合法使用的示例.假设你想为你无法修改的对象模拟其他实例变量(我们正在谈论修改对象本身,即没有子类化).假设在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
Run Code Online (Sandbox Code Playgroud)

此外,是一个非常复杂(但很棒)的方法,使用关联对象与类别..它基本上允许您传入一个块而不是一个选择器到一个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"];
}
Run Code Online (Sandbox Code Playgroud)

奖金:由开创性AFNetworking图书馆的作者Mattt Thompson 看一下相关对象的讨论/解释


Jer*_*myP 5

要回答您修改过的问题:

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

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

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

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

  • 我认为同意你的观点.它有味道.我完全打算这样做.谢谢你的澄清. (2认同)