Как использовать objc_setAssociatedObject / objc_getAssociatedObject внутри объекта? - PullRequest
65 голосов
/ 17 мая 2010

Если я использую objc_setAssociatedObject / objc_getAssociatedObject внутри реализации категории для хранения переменной имитируемого экземпляра в методе сеттера, как я получу доступ к ключу в методе геттера, поскольку любые переменные, объявленные в методе сеттера, будут выходить за рамки области действия геттера метод?

Редактировать: Чтобы уточнить, должен ли я использовать следующий шаблон, где я должен объявить STRING_KEY, чтобы я мог использовать его как в методе установки, так и в методе получения.

@interface NSView (simulateVar)
-(void)setSimualtedString:(NSString *)myString;
-(NSString *)simulatedString;
@end

@implementation NSView (simulateVar)

-(void)setSimualtedString: (NSString *)myString
{
    objc_setAssociatedObject(self, &STRING_KEY, myString, OBJC_ASSOCIATION_RETAIN);
}

-(NSString *)simulatedString
{
    return (NSString *)objc_getAssociatedObject(self, &STRING_KEY);
}

@end

Ответы [ 6 ]

62 голосов
/ 21 мая 2010

Объявите статическую переменную, чтобы вы могли использовать ее адрес в качестве ключа. Вызов objc_setAssociatedObject принимает пустоту *, и фактически используется только адрес вашей статической переменной, а не содержимое строки NSString ..., которая только тратит память.

Вам просто нужно добавить:

static char STRING_KEY; // global 0 initialization is fine here, no 
                        // need to change it since the value of the
                        // variable is not used, just the address
38 голосов
/ 23 мая 2013

Я знаю, что этот вопрос уже устарел, но для полноты картины есть другой способ использования связанных объектов, о которых стоит упомянуть. Это решение использует @selector и, следовательно, нет необходимости в какой-либо дополнительной переменной или константе.

@interface NSObject (CategoryWithProperty)

@property (nonatomic, strong) NSObject *property;

@end

@implementation NSObject (CategoryWithProperty)

- (NSObject *)property {
    return objc_getAssociatedObject(self, @selector(property));
}

- (void)setProperty:(NSObject *)value {
    objc_setAssociatedObject(self, @selector(property), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

(вдохновлено http://www.tuaw.com/2013/04/10/devjuice-better-objective-c-associated-objects/)

22 голосов
/ 15 января 2012

Для связанных ключей хранения void *, мне нравится такой способ:

static void * const kMyAssociatedStorageKey = (void*)&kMyAssociatedStorageKey; 

Это позволяет избежать наличия другой константной строки в исполняемом файле, и, установив ее значение на адрес самого себя, вы получите хорошее уникальное поведение и поведение const (так что вы получите номинальную жалобу компилятора, если вы сделаете что-то, что изменить значение ключа), без каких-либо дополнительных ненужных экспортируемых символов.

16 голосов
/ 17 мая 2010

Объявите статическую переменную (область компиляции) на верхнем уровне исходного файла. Это может помочь сделать это значимым, что-то вроде этого:

static NSString *MYSimulatedString = @"MYSimulatedString";
9 голосов
/ 15 февраля 2013

Довольно близко. Вот полный пример.

.h-файл

@interface NSObject (ExampleCategoryWithProperty)

@property (nonatomic, retain) NSArray *laserUnicorns;

@end

.m-файл

#import <objc/runtime.h>

static void * LaserUnicornsPropertyKey = &LaserUnicornsPropertyKey;

@implementation NSObject (ExampleCategoryWithProperty)

- (NSArray *)laserUnicorns {
    return objc_getAssociatedObject(self, LaserUnicornsPropertyKey);
}

- (void)setLaserUnicorns:(NSArray *)unicorns {
    objc_setAssociatedObject(self, LaserUnicornsPropertyKey, unicorns, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
}

@end

Как обычное свойство - доступно с точечной нотацией

NSObject *myObject = [NSObject new];
myObject.laserUnicorns = @[@"dipwit", @"dipshit"];
NSLog(@"Laser unicorns: %@", myObject.laserUnicorns);

Более простой синтаксис

В качестве альтернативы вы можете использовать @selector(nameOfGetter) вместо создания статического указателя. Зачем? См. https://stackoverflow.com/a/16020927/202451. Пример:

- (NSArray *)laserUnicorns {
    return objc_getAssociatedObject(self, @selector(laserUnicorns));
}

- (void)setLaserUnicorns:(NSArray *)unicorns {
    objc_setAssociatedObject(self, @selector(laserUnicorns), unicorns, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
}
5 голосов
/ 25 сентября 2013

Принятый ответ уже понял его правильно, но я хотел бы добавить объяснение: смысл «ключа» состоит в том, чтобы получить уникальный (для процесса / программы) идентификатор, который не будет иметь случайных коллизий.

Представьте, что ключ объявлен как NSUInteger: многие люди будут использовать стандартные значения, такие как 0, 1 или 42. Особенно, если вы используете какую-то библиотеку или фреймворк, вероятны коллизии.

Поэтому вместо этого у некоторых умных инженеров Apple возникла идея объявить ключ указателем с намерением объявить переменную и передать указатель на эту переменную в качестве ключа. Компоновщик назначит этой переменной уникальный адрес в вашем приложении, поэтому вы гарантированно получите уникальное значение без столкновений.

Фактически, вы можете передать любое значение, которое вам нравится, указатель не разыменовывается (я это проверял). Таким образом, передача (void *)0 или (void *)42 в качестве ключа работает, хотя это не очень хорошая идея (поэтому, пожалуйста, не делайте этого). Для objc_set/getAssociatedObject все, что имеет значение, это то, что значение, переданное в качестве ключа, является уникальным в вашем процессе / программе.

...