Как использовать objc setAssociatedObject/objc getAssociatedObject внутри объекта?



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



Edit: чтобы уточнить, если бы я использовал следующий шаблон, где я должен объявить 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
598   6  

6 ответов:

объявите статическую переменную, чтобы вы могли использовать ее адрес как ключ. Вызов objc_setAssociatedObject принимает значение void* и фактически используется только адрес статической переменной, а не содержимое строки 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

Я знаю, что этот вопрос ушел старый, но я думаю, что для полноты есть еще один способ, как использовать связанные объекты стоит упомянуть. Это решение использует @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/)

для связанного хранения void * ключи, мне нравится этот способ делать это:

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

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

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

static NSString *MYSimulatedString = @"MYSimulatedString";

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

.h-file

@interface NSObject (ExampleCategoryWithProperty)

@property (nonatomic, retain) NSArray *laserUnicorns;

@end

.м-файл

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

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

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

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

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

Comments

    Ничего не найдено.