NSMutableDictionary thread safety

Solution 1:

NSMutableDictionary isn't designed to be thread-safe data structure, and simply marking the property as atomic, doesn't ensure that the underlying data operations are actually performed atomically (in a safe manner).

To ensure that each operation is done in a safe manner, you would need to guard each operation on the dictionary with a lock:

// 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];

You should consider rolling your own NSDictionary that simply delegates calls to NSMutableDictionary while holding a lock:

@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

Please note that because each operation requires waiting for the lock and holding it, it's not quite scalable, but it might be good enough in your case.

If you want to use a proper threaded library, you can use TransactionKit library as they have TKMutableDictionary which is a multi-threaded safe library. I personally haven't used it, and it seems that it's a work in progress library, but you might want to give it a try.

Solution 2:

Nowadays you'd probably go for @synchronized(object) instead.

...
@synchronized(dictionary) {
    [dictionary setObject:image forKey:name];
}
...
@synchronized(dictionary) {
    [dictionary objectForKey:key];
}
...
@synchronized(dictionary) {
    [dictionary removeObjectForKey:key];
}

No need for the NSLock object any more

Solution 3:

after a little bit of research I want to share with you this article :

Using collection classes safely with multithreaded applications http://developer.apple.com/library/mac/#technotes/tn2002/tn2059.html

It looks like notnoop's answer may not be a solution after all. From threading perspective it is ok, but there are some critical subtleties. I will not post here a solution but I guess that there is a good one in this article.

Solution 4:

I have two options to using nsmutabledictionary.

One is:

NSLock* lock = [[NSLock alloc] init];
[lock lock];
[object.dictionary setObject:image forKey:name];
[lock unlock];

Two is:

//Let's assume var image, name are setup properly
dispatch_async(dispatch_get_main_queue(), 
^{ 
        [object.dictionary setObject:image forKey:name];
});

I dont know why some people want to overwrite setting and getting of mutabledictionary.