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?
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:
For read:
For insert:
From Apple Doc about dispatch_barrier_async:
Similar for remove:
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:
Create your serial queue:
Dispatch tasks async to the serial queue:
Hope it helps!
Since serial queues were mentioned: With a mutable array, just asking "is it thread safe" isn't enough. For example, making sure that removeAllObjects doesn't crash is all good and fine, but if another thread tries to process the array at the same time, it will either process the array before or after all elements are removed, and you really have to think what the behaviour should be.
Creating one class + object that is responsible for this array, creating a serial queue for it, and doing all operations through the class on that serial queue is the easiest way to get things right without making your brain hurt through synchronisation problems.
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.This amazing article will explain it all:
http://refactr.com/blog/2012/10/ios-tips-synchronized/
Almost NSMutable classes object is not 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: