我对线程有疑问 安全 </跨度> 同时使用NSMutableDictionary。
主线程是从NSMutableDictionary读取数据,其中:
键是NSString价值是UIImage
异步线程是……
经过一些研究后,我想与大家分享这篇文章:
使用多线程应用程序安全地使用集合类 http://developer.apple.com/library/mac/#technotes/tn2002/tn2059.html
看起来notnoop的答案可能毕竟不是解决方案。从线程角度来看,这是可以的,但有一些关键的微妙之处。我不会在这里发布一个解决方案,但我想在本文中有一个很好的解决方案。
现在你可能会去 @synchronized(object) 代替。
@synchronized(object)
... @synchronized(dictionary) { [dictionary setObject:image forKey:name]; } ... @synchronized(dictionary) { [dictionary objectForKey:key]; } ... @synchronized(dictionary) { [dictionary removeObjectForKey:key]; }
不需要 NSLock 对象了
NSLock
NSMutableDictionary 不是设计为线程安全的数据结构,只是将属性标记为 atomic ,不能确保基础数据操作实际上是以原子方式(以安全的方式)执行的。
NSMutableDictionary
atomic
为确保每个操作都以安全的方式完成,您需要使用锁来保护字典上的每个操作:
// in initialization self.dictionary = [[NSMutableDictionary alloc] init]; // create a lock object for the dictionary self.dictionary_lock = [[NSLock alloc] init]; // at every access or modification: [object.dictionary_lock lock]; [object.dictionary setObject:image forKey:name]; [object.dictionary_lock unlock];
你应该考虑自己动手 NSDictionary 只需在持有锁的情况下将调用委托给NSMutableDictionary:
NSDictionary
@interface SafeMutableDictionary : NSMutableDictionary { NSLock *lock; NSMutableDictionary *underlyingDictionary; } @end @implementation SafeMutableDictionary - (id)init { if (self = [super init]) { lock = [[NSLock alloc] init]; underlyingDictionary = [[NSMutableDictionary alloc] init]; } return self; } - (void) dealloc { [lock_ release]; [underlyingDictionary release]; [super dealloc]; } // forward all the calls with the lock held - (retval_t) forward: (SEL) sel : (arglist_t) args { [lock lock]; @try { return [underlyingDictionary performv:sel : args]; } @finally { [lock unlock]; } } @end
请注意,因为每个操作都需要等待锁定并持有它,所以它不是很可扩展,但在你的情况下它可能已经足够了。
如果要使用正确的线程库,可以使用 TransactionKit库 他们有 TKMutableDictionary 这是一个多线程安全库。我个人还没有使用它,它似乎是一个正在进行中的工作库,但你可能想尝试一下。
TKMutableDictionary
我有两个使用nsmutabledictionary的选项。
一个是:
NSLock* lock = [[NSLock alloc] init]; [lock lock]; [object.dictionary setObject:image forKey:name]; [lock unlock];
二是:
//Let's assume var image, name are setup properly dispatch_async(dispatch_get_main_queue(), ^{ [object.dictionary setObject:image forKey:name]; });
我不知道为什么有些人想要覆盖设置和获取mutabledictionary。
即使答案是正确的,也有一个优雅而不同的解决方案:
- (id)init { self = [super init]; if (self != nil) { NSString *label = [NSString stringWithFormat:@"%@.isolation.%p", [self class], self]; self.isolationQueue = dispatch_queue_create([label UTF8String], NULL); label = [NSString stringWithFormat:@"%@.work.%p", [self class], self]; self.workQueue = dispatch_queue_create([label UTF8String], NULL); } return self; } //Setter, write into NSMutableDictionary - (void)setCount:(NSUInteger)count forKey:(NSString *)key { key = [key copy]; dispatch_async(self.isolationQueue, ^(){ if (count == 0) { [self.counts removeObjectForKey:key]; } else { self.counts[key] = @(count); } }); } //Getter, read from NSMutableDictionary - (NSUInteger)countForKey:(NSString *)key { __block NSUInteger count; dispatch_sync(self.isolationQueue, ^(){ NSNumber *n = self.counts[key]; count = [n unsignedIntegerValue]; }); return count; }
使用线程不安全对象时,副本很重要,这可以避免由于意外释放变量而导致的错误。不需要线程安全实体。
如果更多队列想要使用NSMutableDictionary声明一个私有队列并将setter更改为:
self.isolationQueue = dispatch_queue_create([label UTF8String], DISPATCH_QUEUE_CONCURRENT); - (void)setCount:(NSUInteger)count forKey:(NSString *)key { key = [key copy]; dispatch_barrier_async(self.isolationQueue, ^(){ if (count == 0) { [self.counts removeObjectForKey:key]; } else { self.counts[key] = @(count); } }); }
的 重要! 强>
你必须设置一个自己的私人队列 dispatch_barrier_sync 只是一个简单的 dispatch_sync
详细解释如下 奇妙的博客文章 。