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

时间:2022-08-29 18:06:48

In a project I have taken on, the original author has opted to use objc_setAssociatedObject() and I'm not 100% clear what it does or why they decided to use it.

在我接手的一个项目中,最初的作者选择使用objc_setAssociatedObject(),我不是100%清楚它做了什么,也不清楚他们为什么要使用它。

I decided to look it up and, unfortunately, the docs aren't very descriptive about its purpose.

我决定去查一下,不幸的是,文档并没有很好地描述它的目的。

objc_setAssociatedObject
Sets an associated value for a given object using a given key and association policy.
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)
Parameters
object
The source object for the association.
key
The key for the association.
value
The value to associate with the key key for object. Pass nil to clear an existing association.
policy
The policy for the association. For possible values, see “Associative Object Behaviors.”

objc_setAssociatedObject使用给定的键和关联策略为给定的对象设置关联值。void objc_setAssociatedObject(id对象,void *key, id值,objc_AssociationPolicy policy)参数对象为关联的源对象。为关联键。值与对象的键键相关联的值。传递nil以清除现有的关联。为协会制定政策。对于可能的值,请参见“关联对象行为”。

So what exactly does this function do and in what cases should it be used?

那么这个函数到底是做什么用什么呢?


Edit after reading answers

编辑在阅读答案

So what is the point in the following code?

下面代码的意义是什么呢?

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

What is the point in associating the device with the view controller if it's already an instance variable?

如果设备已经是实例变量,那么将它与视图控制器相关联的意义是什么?

4 个解决方案

#1


31  

From the reference documents on Objective-C Runtime Reference:

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

You use the Objective-C runtime function objc_setAssociatedObject to make an association between one object and another. The function takes four parameters: the source object, a key, the value, and an association policy constant. The key is a void pointer.

使用Objective-C运行时函数objc_setAssociatedObject在一个对象和另一个对象之间建立关联。该函数接受四个参数:源对象、键、值和关联策略常数。关键是一个空指针。

  • The key for each association must be unique. A typical pattern is to use a static variable.
  • 每个关联的键必须是唯一的。典型的模式是使用静态变量。
  • The policy specifies whether the associated object is assigned,
    retained, or copied, and whether the
    association is be made atomically or
    non-atomically. This pattern is
    similar to that of the attributes of
    a declared property (see “Property
    Declaration Attributes”). You specify the policy for the relationship using a constant (see
    objc_AssociationPolicy and
    Associative Object Behaviors).
  • 策略指定是否分配、保留或复制关联对象,以及是否以原子方式或非原子方式进行关联。此模式类似于声明属性的属性(参见“属性声明属性”)。使用常量指定关系的策略(参见objc_AssociationPolicy和关联对象行为)。

Establishing an association between an array and a string

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

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

At point 1, the string overview is still valid because the OBJC_ASSOCIATION_RETAIN policy specifies that the array retains the associated object. When the array is deallocated, however (at point 2), overview is released and so in this case also deallocated. If you try to, for example, log the value of overview, you generate a runtime exception.

在第1点,字符串概述仍然有效,因为OBJC_ASSOCIATION_RETAIN策略指定数组保留关联的对象。但是,在释放数组时(在第2点),会释放overview,因此在本例中也会释放overview。例如,如果您试图记录overview的值,您将生成一个运行时异常。

#2


52  

objc_setAssociatedObject adds a key value store to each Objective-C object. It lets you store additional state for the object, not reflected in its instance variables.

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

It's really convenient when you want to store things belonging to an object outside of the main implementation. One of the main use cases is in categories where you cannot add instance variables. Here you use objc_setAssociatedObject to attach your additional variables to the self object.

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

When using the right association policy your objects will be released when the main object is deallocated.

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

#3


24  

Here is a list of use cases for object associations:

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

one: To add instance variables to categories. In general this technique is advised against, but here is an example of a legitimate use. Let's say you want to simulate additional instance variables for objects you cannot modify (we are talking about modifying the object itself, ie without subclassing). Let's say setting a title on a UIImage.

一:向类别添加实例变量。一般来说,建议反对这种技术,但是这里有一个合法使用的例子。假设您想为不能修改的对象模拟其他实例变量(我们正在讨论修改对象本身,即不进行子类化)。在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

Also, here is a pretty complex (but awesome) way of using associated objects with categories.. it basically allows you to pass in a block instead of a selector to a UIControl.

此外,这里还有一种非常复杂(但非常棒)的方法来使用与类别相关的对象。它允许你传入一个block而不是一个selector给UIControl。


two: Dynamically adding state information to an object not covered by its instance variables in conjunction with KVO.

二:将状态信息与KVO一起动态地添加到实例变量未覆盖的对象中。

The idea is that your object gains state information only during runtime (ie dynamically). So the idea is that although you can store this state info in an instance variable, the fact that you're attaching this info into a an object instantiated at runtime and dynamically associating it with the other object, you are highlighting the fact that this is a dynamic state of the object.

其思想是,对象仅在运行时(即动态)获取状态信息。所以我们的想法是,尽管你可以将这种状态信息存储在一个实例变量,事实上,你把这个信息在运行时实例化一个对象,动态地将它与其他对象,你是强调这样一个事实,这是一个动态的对象。

One excellent example that illustrates this is this library, in which associative objects are used with KVO notifications. Here is an excerpt of the code (note: this KVO notification isn't necessary to run make the code in that library work.. rather it's put there by the author for convenience, basically any object that registers to this will be notified via KVO that changes have happened to it):

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

bonus: take a look at this discussion/explanation of associated objects by Mattt Thompson, author of the seminal AFNetworking library

附注:看看马特·汤普森对相关对象的讨论/解释,他是开创性的AFNetworking库的作者

#4


5  

To answer your revised question:

回答你修改后的问题:

What is the point in associating the device with the view controller if it's already an instance variable?

如果设备已经是实例变量,那么将它与视图控制器相关联的意义是什么?

There are several reasons why you might want to do this.

您可能想这样做的原因有很多。

  • the Device class doesn't have a controller instance variable, or property and you can't change it or subclass it e.g. you don't have the source code.
  • 设备类没有控制器实例变量或属性,您不能更改它或子类,例如,您没有源代码。
  • you want two controllers associated with the device object and you can't change the device class or subclass it.
  • 您想要两个与设备对象关联的控制器,但是您不能更改设备类或它的子类。

Personally, I think it is very rare to need to use low level Objective-C runtime functions. This looks like a code smell to me.

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

#1


31  

From the reference documents on Objective-C Runtime Reference:

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

You use the Objective-C runtime function objc_setAssociatedObject to make an association between one object and another. The function takes four parameters: the source object, a key, the value, and an association policy constant. The key is a void pointer.

使用Objective-C运行时函数objc_setAssociatedObject在一个对象和另一个对象之间建立关联。该函数接受四个参数:源对象、键、值和关联策略常数。关键是一个空指针。

  • The key for each association must be unique. A typical pattern is to use a static variable.
  • 每个关联的键必须是唯一的。典型的模式是使用静态变量。
  • The policy specifies whether the associated object is assigned,
    retained, or copied, and whether the
    association is be made atomically or
    non-atomically. This pattern is
    similar to that of the attributes of
    a declared property (see “Property
    Declaration Attributes”). You specify the policy for the relationship using a constant (see
    objc_AssociationPolicy and
    Associative Object Behaviors).
  • 策略指定是否分配、保留或复制关联对象,以及是否以原子方式或非原子方式进行关联。此模式类似于声明属性的属性(参见“属性声明属性”)。使用常量指定关系的策略(参见objc_AssociationPolicy和关联对象行为)。

Establishing an association between an array and a string

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

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

At point 1, the string overview is still valid because the OBJC_ASSOCIATION_RETAIN policy specifies that the array retains the associated object. When the array is deallocated, however (at point 2), overview is released and so in this case also deallocated. If you try to, for example, log the value of overview, you generate a runtime exception.

在第1点,字符串概述仍然有效,因为OBJC_ASSOCIATION_RETAIN策略指定数组保留关联的对象。但是,在释放数组时(在第2点),会释放overview,因此在本例中也会释放overview。例如,如果您试图记录overview的值,您将生成一个运行时异常。

#2


52  

objc_setAssociatedObject adds a key value store to each Objective-C object. It lets you store additional state for the object, not reflected in its instance variables.

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

It's really convenient when you want to store things belonging to an object outside of the main implementation. One of the main use cases is in categories where you cannot add instance variables. Here you use objc_setAssociatedObject to attach your additional variables to the self object.

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

When using the right association policy your objects will be released when the main object is deallocated.

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

#3


24  

Here is a list of use cases for object associations:

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

one: To add instance variables to categories. In general this technique is advised against, but here is an example of a legitimate use. Let's say you want to simulate additional instance variables for objects you cannot modify (we are talking about modifying the object itself, ie without subclassing). Let's say setting a title on a UIImage.

一:向类别添加实例变量。一般来说,建议反对这种技术,但是这里有一个合法使用的例子。假设您想为不能修改的对象模拟其他实例变量(我们正在讨论修改对象本身,即不进行子类化)。在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

Also, here is a pretty complex (but awesome) way of using associated objects with categories.. it basically allows you to pass in a block instead of a selector to a UIControl.

此外,这里还有一种非常复杂(但非常棒)的方法来使用与类别相关的对象。它允许你传入一个block而不是一个selector给UIControl。


two: Dynamically adding state information to an object not covered by its instance variables in conjunction with KVO.

二:将状态信息与KVO一起动态地添加到实例变量未覆盖的对象中。

The idea is that your object gains state information only during runtime (ie dynamically). So the idea is that although you can store this state info in an instance variable, the fact that you're attaching this info into a an object instantiated at runtime and dynamically associating it with the other object, you are highlighting the fact that this is a dynamic state of the object.

其思想是,对象仅在运行时(即动态)获取状态信息。所以我们的想法是,尽管你可以将这种状态信息存储在一个实例变量,事实上,你把这个信息在运行时实例化一个对象,动态地将它与其他对象,你是强调这样一个事实,这是一个动态的对象。

One excellent example that illustrates this is this library, in which associative objects are used with KVO notifications. Here is an excerpt of the code (note: this KVO notification isn't necessary to run make the code in that library work.. rather it's put there by the author for convenience, basically any object that registers to this will be notified via KVO that changes have happened to it):

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

bonus: take a look at this discussion/explanation of associated objects by Mattt Thompson, author of the seminal AFNetworking library

附注:看看马特·汤普森对相关对象的讨论/解释,他是开创性的AFNetworking库的作者

#4


5  

To answer your revised question:

回答你修改后的问题:

What is the point in associating the device with the view controller if it's already an instance variable?

如果设备已经是实例变量,那么将它与视图控制器相关联的意义是什么?

There are several reasons why you might want to do this.

您可能想这样做的原因有很多。

  • the Device class doesn't have a controller instance variable, or property and you can't change it or subclass it e.g. you don't have the source code.
  • 设备类没有控制器实例变量或属性,您不能更改它或子类,例如,您没有源代码。
  • you want two controllers associated with the device object and you can't change the device class or subclass it.
  • 您想要两个与设备对象关联的控制器,但是您不能更改设备类或它的子类。

Personally, I think it is very rare to need to use low level Objective-C runtime functions. This looks like a code smell to me.

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