Что такое objc_setAssociatedObject () и в каких случаях его следует использовать? - PullRequest
65 голосов
/ 06 мая 2011

В проекте, который я взял на себя, оригинальный автор решил использовать 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 ]

58 голосов
/ 06 мая 2011

objc_setAssociatedObject добавляет хранилище значений ключей к каждому объекту Objective-C. Он позволяет хранить дополнительное состояние для объекта, не отраженное в переменных его экземпляра.

Это действительно удобно, когда вы хотите хранить вещи, принадлежащие объекту, вне основной реализации. Один из основных вариантов использования в категориях, где вы не можете добавить переменные экземпляра. Здесь вы используете objc_setAssociatedObject для присоединения ваших дополнительных переменных к объекту self.

При использовании правильной политики связывания ваши объекты будут освобождены после освобождения основного объекта.

32 голосов
/ 06 мая 2011

Из справочных документов на 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), обзор выпущен и поэтому в этом Дело также освобождено. Если вы попытаетесь, например, введите значение обзор, вы генерируете время выполнения исключение.

25 голосов
/ 01 мая 2013

Вот список вариантов использования для ассоциаций объектов:

one: Чтобы добавить переменные экземпляра в категории. В общем, этот метод рекомендуется против, но вот пример законного использования. Допустим, вы хотите смоделировать дополнительные переменные экземпляра для объектов, которые вы не можете изменить (мы говорим об изменении самого объекта, то есть без подклассов). Допустим, установка заголовка на 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.


two: Динамическое добавление информации о состоянии к объекту, не охватываемому его переменными экземпляра, в сочетании с 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

5 голосов
/ 06 мая 2011

Чтобы ответить на ваш исправленный вопрос:

Какой смысл связывать устройство с контроллером представления, если оно уже является переменной экземпляра?

Существует несколько причинпочему вы можете захотеть сделать это.

  • Класс Device не имеет переменной экземпляра контроллера или свойства, и вы не можете изменить его или сделать его подклассом, например, у вас нет исходного кода.
  • youвам нужно два контроллера, связанных с объектом устройства, и вы не можете изменить класс устройства или подкласс его.

Лично я думаю, что очень редко нужно использовать низкоуровневые функции времени выполнения Objective-C.Для меня это похоже на запах кода.

...