Gab*_*lla 22 objective-c objective-c-runtime associated-object
当使用关联对象,从iOS 4和OSX 10.6开始提供Objective-C运行时功能时,必须定义用于在运行时存储和检索对象的密钥.
典型的用法是定义如下的密钥
static char const * const ObjectTagKey = "ObjectTag";
Run Code Online (Sandbox Code Playgroud)
然后使用是存储对象
objc_setAssociatedObject(self, ObjectTagKey, newObjectTag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
Run Code Online (Sandbox Code Playgroud)
并检索它
objc_getAssociatedObject(self, ObjectTagKey);
Run Code Online (Sandbox Code Playgroud)
(例如http://oleb.net/blog/2011/05/faking-ivars-in-objc-categories-with-associative-references/)
是否有更简洁的方法来定义关联的对象键,这不涉及额外变量的声明?
Gab*_*lla 49
根据Erica Sadun的博客文章(其归功于Gwynne Raskind),有.
objc_getAssociatedObject并objc_getAssociatedObject需要一个键来存储对象.这样的密钥需要是一个常量void指针.所以最后我们只需要一个随时间保持不变的固定地址.
事实证明,@selector实现提供了我们需要的东西,因为它使用固定地址.
因此,我们可以摆脱密钥声明,只需使用我们的属性的选择器地址.
因此,如果您在运行时关联属性,如
@property (nonatomic, retain) id anAssociatedObject;
Run Code Online (Sandbox Code Playgroud)
我们可以为它的getter/setter提供动态实现
- (void)setAnAssociatedObject:(id)newAssociatedObject {
objc_setAssociatedObject(self, @selector(anAssociatedObject), newAssociatedObject, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)anAssociatedObject {
return objc_getAssociatedObject(self, @selector(anAssociatedObject));
}
Run Code Online (Sandbox Code Playgroud)
非常整洁,绝对比为每个关联对象定义额外的静态变量键更清晰.
由于这是依赖于实现的,一个合理的问题是:它会轻易破解吗?引用博客条目
Apple可能不得不实施一个全新的ABI来实现这一目标
如果我们认为这些话是真的,那么它就是相当安全的.
如果您需要从单个方法范围之外访问密钥,那么一个很好的模式可以使代码更易读,就是创建一个指针,它只是指向堆栈中自己的地址.例如:
static void const *MyAssocKey = &MyAssocKey;
Run Code Online (Sandbox Code Playgroud)
如果您只需要在单个方法的范围内进行访问,您实际上可以使用_cmd,这保证是唯一的.例如:
objc_setAssociatedObject(obj, _cmd, associatedObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
Run Code Online (Sandbox Code Playgroud)
@Gabriele Petronella讨论的想法略有不同,就是将字典与每个对象相关联:
//NSObject+ADDLAssociatedDictionary.h
#import <Foundation/Foundation.h>
@interface NSObject (ADDLAssociatedDictionary)
- (void)addl_setAssociatedObject:(id)object forKey:(id<NSCopying>)key;
- (id)addl_associatedObjectForKey:(id<NSCopying>)key;
@end
//NSObject+ADDLAssociatedDictionary.m
#import <objc/runtime.h>
@interface NSObject (ADDLAssociatedDictionaryInternal)
- (NSMutableDictionary *)addl_associatedDictionary;
@end
@implementation NSObject (ADDLAssociatedDictionary)
- (void)addl_setAssociatedObject:(id)object forKey:(id<NSCopying>)key
{
if (object) {
self.addl_associatedDictionary[key] = object;
} else {
[self.addl_associatedDictionary removeObjectForKey:key];
}
}
- (id)addl_associatedObjectForKey:(id<NSCopying>)key
{
return self.addl_associatedDictionary[key];
}
@end
@implementation NSObject (ADDLAssociatedDictionaryInternal)
const char addl_associatedDictionaryAssociatedObjectKey;
- (NSMutableDictionary *)addl_associatedDictionaryPrimitive
{
return objc_getAssociatedObject(self, &addl_associatedDictionaryAssociatedObjectKey);
}
- (void)addl_setAssociatedDictionaryPrimitive:(NSMutableDictionary *)associatedDictionary
{
objc_setAssociatedObject(self, &addl_associatedDictionaryAssociatedObjectKey, associatedDictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSMutableDictionary *)addl_generateAssociatedDictionary
{
NSMutableDictionary *associatedDictionary = [[NSMutableDictionary alloc] init];
[self addl_setAssociatedDictionaryPrimitive:associatedDictionary];
return associatedDictionary;
}
- (NSMutableDictionary *)addl_associatedDictionary
{
NSMutableDictionary *res = nil;
@synchronized(self) {
if (!(res = [self addl_associatedDictionaryPrimitive])) {
res = [self addl_generateAssociatedDictionary];
}
}
return res;
}
@end
Run Code Online (Sandbox Code Playgroud)
然后在我们的类别中的一些子类Derived of NSObject
//Derived+Additions.h
#import "Derived.h"
@interface Derived (Additions)
@property (nonatomic) id anAssociatedObject;
@end
//Derived+Additions.m
#import "NSObject+ADDLAssociatedDictionary.h"
@implementation Derived (Additions)
- (void)setAnAssociatedObject:(id)anAssociatedObject
{
[self addl_setAssociatedObject:anAssociatedObject forKey:NSStringFromSelector(@selector(anAssociatedObject))];
}
- (id)anAssociatedObject
{
return [self addl_associatedObjectForKey:NSStringFromSelector(@selector(anAssociatedObject))];
}
@end
Run Code Online (Sandbox Code Playgroud)
一般来说,相关字典方法的一个好处是,能够为运行时生成的键设置对象,而不是提及更好的语法,从而增加了灵活性.
使用特别有益
NSStringFromSelector(@selector(anAssociatedObject))
Run Code Online (Sandbox Code Playgroud)
是NSStringFromSelector保证给出NSString选择器的表示,它始终是可接受的字典键.因此,我们不必担心ABI的变化(尽管我认为这不是一个合理的考虑因素).
| 归档时间: |
|
| 查看次数: |
7634 次 |
| 最近记录: |