Objective-C 运行时将对象与 NSMutable 字典关联
Objective-C runtime associate object with NSMutable dictionary
引用自此 post link
我使用 NSMutableDictionary 存储我需要的信息的相同概念实现了一个类似的类别。但是原文有一点让我很困惑post
- (NSMutableDictionary *)addl_associatedDictionary
{
NSMutableDictionary *res = nil;
@synchronized(self)
{
if (!(res = [self addl_associatedDictionaryPrimitive]))
{
res = [self addl_generateAssociatedDictionary];
}
}
return res;
}
我知道@synchronized 关键字是对多线程的保护。但是当我浏览其他示例时,大多数都没有使用保护。那么保护有必要吗?我也可以使用 static dispatch_once_t 来替换 @synchronized 吗?下面是我在 .m 文件中的代码片段
@dynamic associatedDict;
-(void)setAssociateValue:(NSMutableDictionary*)dict
{
objc_setAssociatedObject(self, @selector(associatedDict), dict, OBJC_ASSOCIATION_RETAIN);
}
-(id)getAssociateValue
{
return objc_getAssociatedObject(self, @selector(associatedDict));
}
-(NSMutableDictionary*)associatedDict
{
NSMutableDictionary* dict=[self getAssociateValue];
if(!dict)
{
dict=[[NSMutableDictionary alloc]init];
[self setAssociatedDict:dict];
}
return dict;
}
-(void)setAssociateDict:(NSMutableDictionary *)associatedDict
{
[self setAssociatedDict:associatedDict];
}
-(id)associate_getObjectForKey:(NSString*)key
{
return self.associatedDict[key];
}
-(void)associate_setObject:(id)obj forKey:(NSString*)key
{
[self.associatedDict setObject:obj forKey:key];
}
向后退,不,您不能简单地使用 dispatch_once_t
来完成此特定任务。正确使用 dispatch_once_t
需要一个全局变量,并且您的任务需要为每个对象实例完成一次 - 即您需要为每个实例使用一个唯一的全局变量...
需要@synchronized
的保护吗?这是为了防止两个或多个线程同时创建字典。如果没有它在每个线程的第一次调用中,当然取决于时间,每个线程可能 return 一个不同的字典。在随后的调用中,每个调用都会 return 最后一个线程创建的字典以对关联变量进行赋值,所有其他创建的字典都将丢失。
重要:NSMutableDictionary
本身不是线程安全的。如果您确实有多个线程读取和写入字典,那么您需要额外的 synchronisation/locking 以避免出现问题。有多种方法可以做到这一点,只需搜索并找到适合您需求的解决方案即可。当然,如果您没有多线程,那么所有这些都没有实际意义,NSMutableDictionary
可以并且一直被安全使用,而不是线程安全的。
HTH
例如它可能有助于回答关于锁定成本的隐含问题:我注意到您使用的是 OBJC_ASSOCIATION_RETAIN
而不是 OBJC_ASSOCIATION_RETAIN_NONATOMIC
。考虑到您的 @synchronize
,这似乎是多余的,因为如果您拥有后者,则可以放弃对前者的锁定。此时您要为同步支付两次费用。要么付一次,要么不付。
最佳整体解决方案可能是:
NSMutableDictionary *res; // no need to assign to `nil`; it's implied in ARC
// you're using an atomic property, so this is inherently safe
if (!(res = [self addl_associatedDictionaryPrimitive]))
{
// okay, doesn't exist, but two or more threads may get to
// here simultaneously so we'll need to synchronise, and...
@synchronized(self)
{
// ... check again. As someone else may already have proceeded past the
// outer if and created it while this thread was waiting to
// enter the synchronised block. Assuming this dictionary
// is created more rarely than it's accessed, this is no great issue
if (!(res = [self addl_associatedDictionaryPrimitive]))
{
res = [self addl_generateAssociatedDictionary];
}
}
}
return res;
... 并坚持 OBJC_ASSOCIATION_RETAIN
。还要注意 CRD 的一点:可变字典本身不是线程安全的。因此,如果您确实需要线程安全,那么这并不能完全解决问题。如果你不需要线程安全,那么切换到 OBJC_ASSOCIATION_RETAIN_NONATOMIC
并转储 @synchronized
.
引用自此 post link 我使用 NSMutableDictionary 存储我需要的信息的相同概念实现了一个类似的类别。但是原文有一点让我很困惑post
- (NSMutableDictionary *)addl_associatedDictionary
{
NSMutableDictionary *res = nil;
@synchronized(self)
{
if (!(res = [self addl_associatedDictionaryPrimitive]))
{
res = [self addl_generateAssociatedDictionary];
}
}
return res;
}
我知道@synchronized 关键字是对多线程的保护。但是当我浏览其他示例时,大多数都没有使用保护。那么保护有必要吗?我也可以使用 static dispatch_once_t 来替换 @synchronized 吗?下面是我在 .m 文件中的代码片段
@dynamic associatedDict;
-(void)setAssociateValue:(NSMutableDictionary*)dict
{
objc_setAssociatedObject(self, @selector(associatedDict), dict, OBJC_ASSOCIATION_RETAIN);
}
-(id)getAssociateValue
{
return objc_getAssociatedObject(self, @selector(associatedDict));
}
-(NSMutableDictionary*)associatedDict
{
NSMutableDictionary* dict=[self getAssociateValue];
if(!dict)
{
dict=[[NSMutableDictionary alloc]init];
[self setAssociatedDict:dict];
}
return dict;
}
-(void)setAssociateDict:(NSMutableDictionary *)associatedDict
{
[self setAssociatedDict:associatedDict];
}
-(id)associate_getObjectForKey:(NSString*)key
{
return self.associatedDict[key];
}
-(void)associate_setObject:(id)obj forKey:(NSString*)key
{
[self.associatedDict setObject:obj forKey:key];
}
向后退,不,您不能简单地使用 dispatch_once_t
来完成此特定任务。正确使用 dispatch_once_t
需要一个全局变量,并且您的任务需要为每个对象实例完成一次 - 即您需要为每个实例使用一个唯一的全局变量...
需要@synchronized
的保护吗?这是为了防止两个或多个线程同时创建字典。如果没有它在每个线程的第一次调用中,当然取决于时间,每个线程可能 return 一个不同的字典。在随后的调用中,每个调用都会 return 最后一个线程创建的字典以对关联变量进行赋值,所有其他创建的字典都将丢失。
重要:NSMutableDictionary
本身不是线程安全的。如果您确实有多个线程读取和写入字典,那么您需要额外的 synchronisation/locking 以避免出现问题。有多种方法可以做到这一点,只需搜索并找到适合您需求的解决方案即可。当然,如果您没有多线程,那么所有这些都没有实际意义,NSMutableDictionary
可以并且一直被安全使用,而不是线程安全的。
HTH
例如它可能有助于回答关于锁定成本的隐含问题:我注意到您使用的是 OBJC_ASSOCIATION_RETAIN
而不是 OBJC_ASSOCIATION_RETAIN_NONATOMIC
。考虑到您的 @synchronize
,这似乎是多余的,因为如果您拥有后者,则可以放弃对前者的锁定。此时您要为同步支付两次费用。要么付一次,要么不付。
最佳整体解决方案可能是:
NSMutableDictionary *res; // no need to assign to `nil`; it's implied in ARC
// you're using an atomic property, so this is inherently safe
if (!(res = [self addl_associatedDictionaryPrimitive]))
{
// okay, doesn't exist, but two or more threads may get to
// here simultaneously so we'll need to synchronise, and...
@synchronized(self)
{
// ... check again. As someone else may already have proceeded past the
// outer if and created it while this thread was waiting to
// enter the synchronised block. Assuming this dictionary
// is created more rarely than it's accessed, this is no great issue
if (!(res = [self addl_associatedDictionaryPrimitive]))
{
res = [self addl_generateAssociatedDictionary];
}
}
}
return res;
... 并坚持 OBJC_ASSOCIATION_RETAIN
。还要注意 CRD 的一点:可变字典本身不是线程安全的。因此,如果您确实需要线程安全,那么这并不能完全解决问题。如果你不需要线程安全,那么切换到 OBJC_ASSOCIATION_RETAIN_NONATOMIC
并转储 @synchronized
.