Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Objective-C's NSMutableArray thread-safe?

I've been trying to fix this crash for almost a week. The application crashes without any exception or stack-trace. The application does not crash in any way while running through instruments in zombie mode.

I have a method that gets called on a different thread. The solution that fixed the crash was replacing

[self.mutableArray removeAllObjects]; 

with

dispatch_async(dispatch_get_main_queue(), ^{     [self.searchResult removeAllObjects]; }); 

I thought it might be a timing issue, so I tried to synchronize it, but it still crashed:

@synchronized(self) {     [self.searchResult removeAllObjects]; } 

Here is the code

- (void)populateItems {    // Cancel if already exists      [self.searchThread cancel];     self.searchThread = [[NSThread alloc] initWithTarget:self                                                selector:@selector(populateItemsinBackground)                                                  object:nil];      [self.searchThread start]; }   - (void)populateItemsinBackground {     @autoreleasepool     {         if ([[NSThread currentThread] isCancelled])             [NSThread exit];          [self.mutableArray removeAllObjects];          // Populate data here into mutable array          for (loop here)         {             if ([[NSThread currentThread] isCancelled])                 [NSThread exit];              // Add items to mutableArray         }     } } 

Is this problem with NSMutableArray not being thread-safe?

like image 227
aryaxt Avatar asked Aug 23 '12 18:08

aryaxt


People also ask

Is Objective C thread-safe?

It is unsafe for one thread to be reading an array while another thread appends to it.

What is NSMutableArray Objective C?

The NSMutableArray class declares the programmatic interface to objects that manage a modifiable array of objects. This class adds insertion and deletion operations to the basic array-handling behavior inherited from NSArray . NSMutableArray is “toll-free bridged” with its Core Foundation counterpart, CFMutableArray .

Is list Addrange thread-safe?

No it is not thread-safe.


2 Answers

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!

like image 43
Jingjie Zhan Avatar answered Oct 12 '22 23:10

Jingjie Zhan


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]; 
like image 114
Daniel Avatar answered Oct 13 '22 01:10

Daniel