如何在对象中使用objc_setAssociatedObject / objc_getAssociatedObject?

如果我在一个类别实现中使用objc_setAssociatedObject / objc_getAssociatedObject在setter方法中存储模拟的实例variables,我将如何访问getter方法中的键,因为在setter方法中声明的任何variables将超出getter方法的范围?

编辑:澄清,如果我要使用以下模式,我应该在哪里申报STRING_KEY,以便我可以在setter和getter方法中使用它。

@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 

声明一个静态variables,以便您可以使用它的地址作为关键字。 对objc_setAssociatedObject的调用需要一个void *,只有你的静态variables的地址被实际使用,而不是一个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 

我知道这个问题已经过时了,但是我认为完整性还有另外一个方法,那就是如何使用值得一提的关联对象。 这个解决scheme使用@selector ,因此不需要任何额外的variables或常量。

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

这样可以避免在可执行文件中使用另一个常量string,并且通过将其值设置为自身的地址,可以获得良好的uniquing和const行为(所以如果您做了一些可能会改变键),没有任何额外的不必要的导出符号。

在源文件的顶层声明一个静态(编译单元范围)variables。 这可能有助于使其有意义,如下所示:

 static NSString *MYSimulatedString = @"MYSimulatedString"; 

相当接近。 这是一个完整的例子。

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

语法更简单

或者,您可以使用@select器@selector(nameOfGetter)而不是创build一个静态指针。 为什么? 请参阅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 :很多人会使用像042这样的标准值。 特别是如果你使用一些库或框架,可能会发生冲突。

相反,一些聪明的苹果工程师有这样的想法:将声明的关键字声明为一个指针,意图是声明一个variables并将指针指向该variables作为关键字。 链接器将为该variables分配一个在您的应用程序中唯一的地址,因此您可以保证获得唯一的,无冲突的值。

事实上,你可以传递任何你喜欢的值,指针不被解除引用(我testing过了)。 所以通过(void *)0(void *)42作为关键的工作,虽然这不是一个好主意(所以请不要这样做)。 对于objc_set/getAssociatedObject ,所有重要的是,作为密钥传递的值在您的过程/程序中是唯一的。