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

leo*_*leo 65 objective-c categories

如果我在类别实现中使用objc_setAssociatedObject/objc_getAssociatedObject将模拟实例变量存储在setter方法中,那么我将如何访问getter方法中的键,因为setter方法中声明的任何变量都不在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
Run Code Online (Sandbox Code Playgroud)

小智 62

声明一个静态变量,以便您可以使用其地址作为键.对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
Run Code Online (Sandbox Code Playgroud)


zla*_*ajo 38

我知道这个问题已经过时了,但我认为完整性还有另一种方法可以使用值得一提的相关对象.该解决方案利用@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
Run Code Online (Sandbox Code Playgroud)

(受http://www.tuaw.com/2013/04/10/devjuice-better-objective-c-associated-objects/启发)

  • 谢谢你的解决方案!似乎是对我来说最清晰的. (2认同)

ipm*_*mcc 23

对于关联的存储void *密钥,我喜欢这种方式:

static void * const kMyAssociatedStorageKey = (void*)&kMyAssociatedStorageKey; 
Run Code Online (Sandbox Code Playgroud)

这避免了在可执行文件中有另一个常量字符串,并通过将其值设置为自身的地址,您可以获得良好的单一性和const行为(因此,如果您执行可能会更改密钥值的内容,您将在名义上获得编译器投诉),没有任何额外的不必要的导出符号.


Nic*_*ley 16

在源文件的顶级声明一个静态(编译单元范围)变量.它可能有助于使其有意义,如下所示:

static NSString *MYSimulatedString = @"MYSimulatedString";
Run Code Online (Sandbox Code Playgroud)

  • 由于这个答案没有被接受,澄清一下:让变量的值有意义是很好的,因为你可以从调试器打印它,即使你没有编译到你的程序中的调试符号. (2认同)

hfo*_*sli 9

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

.H文件

@interface NSObject (ExampleCategoryWithProperty)

@property (nonatomic, retain) NSArray *laserUnicorns;

@end
Run Code Online (Sandbox Code Playgroud)

.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
Run Code Online (Sandbox Code Playgroud)

就像一个普通的财产 - 可用点符号访问

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

语法更简单

或者,您可以使用@selector(nameOfGetter)而不是创建静态指针.为什么?请参阅/sf/answers/1121464921/.例:

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

- (void)setLaserUnicorns:(NSArray *)unicorns {
    objc_setAssociatedObject(self, @selector(laserUnicorns), unicorns, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
}
Run Code Online (Sandbox Code Playgroud)


Dar*_*ust 5

已接受的答案已经正确,但我想补充说明:"关键"的要点是获得一个不会发生意外碰撞的唯一(每个进程/程序)标识符.

想象一下,密钥被声明为NSUInteger:许多人会使用标准值,如0,142.特别是如果您使用某些库或框架,可能会发生冲突.

因此,一些聪明的Apple工程师有想法将密钥声明为指针,其意图是声明变量并将指针作为键传递给该变量.链接器将为该变量分配一个在您的应用程序中唯一的地址,因此您可以保证获得唯一的无冲突值.

实际上,你可以传递你喜欢的任何值,指针不会被解引用(我已经测试过了).所以传递(void *)0(void *)42作为关键确实有效,虽然这不是一个好主意(所以请不要这样做).因为objc_set/getAssociatedObject,重要的是作为键传递的值在您的流程/程序中是唯一的.