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

发布于 2024-09-01 23:03:47 字数 678 浏览 6 评论 0原文

如果我在类别实现中使用 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

If I use objc_setAssociatedObject/objc_getAssociatedObject inside a category implementation to store a simulated instance variable in a setter method, how would I access the key in the getter method since any variables declared in the setter method would be outside the scope of the getter method?

Edit: To clarify, if I were to use the following pattern, where should I declare STRING_KEY so that I could use it in both the setter and the getter method.

@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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

热血少△年 2024-09-08 23:03:47

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

Declare a static variable so that you can use its address as the key.
The call to objc_setAssociatedObject takes a void* and only the address of your static variable is actually used, not the contents of a NSString... that is only wasting memory.

You just need to add:

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
尐籹人 2024-09-08 23:03:47

我知道这个问题已经过时了,但我认为为了完整性,还有另一种方法如何使用值得一提的关联对象。该解决方案利用@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-linked-objects/)

I know that this question is quit old but I think for completeness there is another way how to use associated objects worth mentioning. This solution utilizes @selector and therefore there is no need for any extra variable or constant.

@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

(inspired by http://www.tuaw.com/2013/04/10/devjuice-better-objective-c-associated-objects/)

凹づ凸ル 2024-09-08 23:03:47

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

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

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

For associated storage void * keys, I like this way of doing it:

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

This avoids having another constant string in the executable, and by setting its value to the address of itself, you get good uniquing, and const behavior (so you'll nominally get a compiler complaint if you do something that would change the value of the key), without any extra unnecessary exported symbols.

万劫不复 2024-09-08 23:03:47

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

static NSString *MYSimulatedString = @"MYSimulatedString";

Declare a static (compilation unit-scope) variable at the top level of the source file. It may help to make it meaningful, something like this:

static NSString *MYSimulatedString = @"MYSimulatedString";
娇纵 2024-09-08 23:03:47

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

.h-file

@interface NSObject (ExampleCategoryWithProperty)

@property (nonatomic, retain) NSArray *laserUnicorns;

@end

.m-file

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

Quite close. Here is a full example.

.h-file

@interface NSObject (ExampleCategoryWithProperty)

@property (nonatomic, retain) NSArray *laserUnicorns;

@end

.m-file

#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

Just like a normal property - accessible with dot-notation

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

Easier syntax

Alternatively you could use @selector(nameOfGetter) instead of creating a static pointer. Why? See https://stackoverflow.com/a/16020927/202451. Example:

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

- (void)setLaserUnicorns:(NSArray *)unicorns {
    objc_setAssociatedObject(self, @selector(laserUnicorns), unicorns, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
}
余罪 2024-09-08 23:03:47

接受的答案已经正确,但我想添加一个解释:“关键”的要点是获得一个唯一的(每个进程/程序)标识符,不会有任何意外冲突。

想象一下密钥被声明为 NSUInteger:很多人会使用 0142 等标准值。特别是如果您正在使用某些库或框架,则很可能会发生冲突。

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

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

The accepted answer already got it right, but I'd like to add an explanation: the point of the "key" is to get a unique (per process/program) identifier that will not have any accidental collisions.

Imagine the key being declared as NSUInteger: lots of people would use standard values like 0, 1 or 42. Especially if you're using some library or framework, collisions are likely.

So instead, some clever Apple engineer had the idea to declare the key to be a pointer with the intent that you declare a variable and pass the pointer to that variable as a key. The linker will assign that variable an address that is unique within your application and so you are guaranteed to get a unique, collision free value.

In fact, you can pass any value you like, the pointer is not dereferenced (I've tested that). So passing (void *)0 or (void *)42 as the key does work, although it's not a good idea (so please don't do that). For objc_set/getAssociatedObject, all that matters is that the value passed as key is unique within your process/program.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文