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

Objective c 如何在对象中使用objc_setAssociatedObject/objc_getAssociatedObject?,objective-c,categories,Objective C,Categories,如果我在类别实现中使用objc_setAssociatedObject/objc_getAssociatedObject在setter方法中存储模拟实例变量,那么如何访问getter方法中的键,因为setter方法中声明的任何变量都不在getter方法的范围内 Edit:为了澄清,如果要使用以下模式,我应该在哪里声明STRING\u键,以便在setter和getter方法中使用它 @interface NSView (simulateVar) -(void)setSimualtedString:

如果我在类别实现中使用objc_setAssociatedObject/objc_getAssociatedObject在setter方法中存储模拟实例变量,那么如何访问getter方法中的键,因为setter方法中声明的任何变量都不在getter方法的范围内

Edit:为了澄清,如果要使用以下模式,我应该在哪里声明STRING\u键,以便在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
在源文件的顶层声明静态(编译单元范围)变量。它可能有助于使其有意义,例如:

static NSString *MYSimulatedString = @"MYSimulatedString";

声明一个静态变量,以便可以使用其地址作为键。 对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

对于相关的存储
void*
键,我喜欢这样做:

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

这避免了在可执行文件中有另一个常量字符串,通过将其值设置为其自身的地址,您可以获得良好的uniquing和
const
行为(因此,如果您做了会更改键值的事情,您名义上会收到编译器的投诉),而不需要任何额外的不必要的导出符号。

非常接近。这里有一个完整的例子

.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
.m-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

更简单的语法

或者,您可以使用
@selector(nameOfGetter)
而不是创建静态指针。为什么?看见例如:

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

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

我知道这个问题已经过时了,但我认为为了完整起见,还有另一种方法可以使用相关对象,值得一提。此解决方案利用了
@选择器
,因此不需要任何额外的变量或常量

@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

(受启发)

公认的答案已经正确了,但我想补充一个解释:“键”的意义在于获得一个唯一的(每个进程/程序)标识符,该标识符不会发生任何意外冲突

假设密钥被声明为
NSUInteger
:许多人会使用标准值,如
0
1
42
。特别是当您使用某个库或框架时,可能会发生冲突

因此,一些聪明的苹果工程师想出了一个主意,将键声明为指针,目的是声明一个变量,并将指针作为键传递给该变量。链接器将为该变量分配一个在您的应用程序中唯一的地址,因此保证您获得一个唯一的、无冲突的值


事实上,您可以传递任何您喜欢的值,指针没有被取消引用(我已经测试过了)。因此,传递
(void*)0
(void*)42
作为键确实有效,尽管这不是一个好主意(因此请不要这样做)。对于
objc\u set/getAssociatedObject
,重要的是作为键传递的值在您的进程/程序中是唯一的。

我用回答您问题的答案替换了我的答案,现在我知道它是什么:-)谢谢!这似乎很明显。我的大脑似乎想把所有的东西都封装起来:)。既然这个答案没有被接受,那么澄清一下:让变量的值有意义很好,因为你可以从调试器打印它,即使您的程序中没有编译调试符号。上面的答案最初引用了指向不再存在的apple文档的链接。。是一个示例,说明了上述答案,尽管它不在类别设置器/获取器上下文中。如果有人有更好的例子,请分享!由于某些原因,网络上没有广泛涉及关联对象的主题。我认为这是一个非常接近OP要求的例子:另一个不错的键是选择器-我已经使用了一段时间,我在一些博客(Mike Ash?Mattt Thompson?)上读到了这一点。谢谢你的解决方案!对我来说似乎是最清楚的。最好的答案!只有一个为我解决了这个问题。