Is Objective-C's NSMutableArray thread-safe?
No.
It is not thread safe and if you need to modify your mutable array from another thread you should use NSLock
to ensure everything goes as planned:
NSLock *arrayLock = [[NSLock alloc] init];
[...]
[arrayLock lock]; // NSMutableArray isn't thread-safe
[myMutableArray addObject:@"something"];
[myMutableArray removeObjectAtIndex:5];
[arrayLock unlock];
As others already said, NSMutableArray is not thread safe. In case anyone want to achieve more than removeAllObject in a thread-safe environment, I will give another solution using GCD besides the one using lock. What you have to do is to synchronize the read/update(replace/remove) actions.
First get the global concurrent queue:
dispatch_queue_t concurrent_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
For read:
- (id)objectAtIndex:(NSUInteger)index {
__block id obj;
dispatch_sync(self.concurrent_queue, ^{
obj = [self.searchResult objectAtIndex:index];
});
return obj;
}
For insert:
- (void)insertObject:(id)obj atIndex:(NSUInteger)index {
dispatch_barrier_async(self.concurrent_queue, ^{
[self.searchResult insertObject:obj atIndex:index];
});
}
From Apple Doc about dispatch_barrier_async:
When the barrier block reaches the front of a private concurrent queue, it is not executed immediately. Instead, the queue waits until its currently executing blocks finish executing. At that point, the barrier block executes by itself. Any blocks submitted after the barrier block are not executed until the barrier block completes.
Similar for remove:
- (void)removeObjectAtIndex:(NSUInteger)index {
dispatch_barrier_async(self.concurrent_queue, ^{
[self.searchResult removeObjectAtIndex:index];
});
}
EDIT: Actually I found another simpler way today to synchronize access to a resource by using a serial queue provided by GCD.
From Apple Doc Concurrency Programming Guide > Dispatch Queues:
Serial queues are useful when you want your tasks to execute in a specific order. A serial queue executes only one task at a time and always pulls tasks from the head of the queue. You might use a serial queue instead of a lock to protect a shared resource or mutable data structure. Unlike a lock, a serial queue ensures that tasks are executed in a predictable order. And as long as you submit your tasks to a serial queue asynchronously, the queue can never deadlock.
Create your serial queue:
dispatch_queue_t myQueue = dispatch_queue_create("com.example.MyQueue", NULL);
Dispatch tasks async to the serial queue:
dispatch_async(myQueue, ^{
obj = [self.searchResult objectAtIndex:index];
});
dispatch_async(myQueue, ^{
[self.searchResult removeObjectAtIndex:index];
});
Hope it helps!
As well as NSLock
can also use @synchronized
(condition-object) you just have to make sure every access of the array is wrapped in a @synchronized
with the same object acting as the condition-object , if you only want to modify the contents of the same array instance then you can use the array itself as the condition-object, other wise you will have to use something else you know will not go away, the parent object, i.e self, is a good choice because it will always be the same one for the same array.
atomic in @property
attributes will only make setting the array thread safe not modifying the contents, i.e. self.mutableArray
= ... is thread safe but [self.mutableArray removeObject:]
is not.