Objective-C运行时关联对象与NSMutable字典

时间:2015-07-02 18:47:01

标签: ios objective-c concurrency associated-object

此帖link的参考 我使用与使用NSMutableDictionary相同的概念来实现类似的类别来存储我需要的信息。但是有一件事让我在原帖中感到困惑

- (NSMutableDictionary *)addl_associatedDictionary
{
     NSMutableDictionary *res = nil;
     @synchronized(self) 
     {
        if (!(res = [self addl_associatedDictionaryPrimitive])) 
        {
           res = [self addl_generateAssociatedDictionary];
        }
     }
    return res;
}

我知道@synchronized关键字是对mutilthread的保护。但是当我通过其他例子时,大多数人都没有使用保护措施。那么保护是必要的吗?我也可以使用静态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];
}

2 个答案:

答案 0 :(得分:2)

向后看,不能简单地使用dispatch_once_t来执行此特定任务。正确使用dispatch_once_t需要一个全局变量,每个对象实例需要完成一次任务 - 即每个实例都需要一个唯一的全局...

您需要@synchronized的保护吗?这是为了防止两个或多个线程都创建字典。如果没有它来自每个线程的第一次调用,根据当然的时间,每个可能返回一个不同的字典。在后续调用中,每个调用都将返回由最后一个线程创建的字典以对相关变量进行赋值,所有其他创建的字典都将丢失。

重要NSMutableDictionary本身线程安全。如果你有多个线程阅读&编写字典然后你需要额外的同步/锁定来避免问题。有多种方法可以做到这一点,只需搜索并找到符合您需求的解决方案。当然,如果你没有多个线程,这一切都没有用,NSMutableDictionary可以并且一直安全使用,而不是线程安全的。

HTH

答案 1 :(得分:1)

这可能有助于回答隐含的锁定成本问题:我注意到您使用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