NSMutableArray mutation crash - ios

I'm facing "Collection was mutated while being enumerated" crash in dispatch_async method. This is the code:
- (void) addCutObjectFromObject: (Object *) object {
dispatch_async(self.objectsQueue, ^{
ObjectCut *objectCut = [[[ObjectCut alloc] init] autorelease];
objectCut.objectId = object.uid;
objectCut.objectName = object.internal.name;
#synchronized(self.shownChannelsArray) {
if (![self.objectsArray containsObject: objectCut])
{
[self.objectsArray addObject: objectCut];
}
}
});
}
Dispatch_queue is created in the init method like this:
_objectsQueue = dispatch_queue_create("objectsQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_retain(_objectsQueue);
The code crashes without synchronized block. As far as I know, it should not crash, because there is no loop, and blocks should be added one-by-one waiting for their queue. Can someone explain me, why that is happening?

dispatch_queue_create("objectsQueue", DISPATCH_QUEUE_CONCURRENT) creates a concurrent queue. Arbitrarily many blocks dispatched to that queue may execute simultaneously. It's likely that containsObject: uses enumeration internally to do a linear search. Therefore one block is in the middle of containsObject: while another performs addObject:. NSMutableArrays are, like the other Foundation mutable objects, not guaranteed to be thread safe.
At the very least you want to use DISPATCH_QUEUE_SERIAL. Unless the order is really important to you or your objects don't implement hash, isEqual:, etc, you should probably also just use a set rather than sort of manually recreating one with an array.

Aren't you calling addCutObjectFromObject: from a loop or an enumeration ?
If It is, you may want to check this part.

Ah a tricky one.. from my knowledge (and I may be a bit off, so others, feel free to follow up, but this poor chap doesnt have any responses yet..
so when this executes, the async block will not run right away. It will first be scheduled, I would imagine you are calling this addCutObjectFromObject method more than once. Let's say for example sake that you are calling this method 5 times. Now think about threads. The first call gets scheduled, eventually begins running, while the second call gets scheduled, eventually starts running, etc.. and this is where the problem starts and I believe your culprit is in containsObject and addObject. Under the hood, containsObject is iterating through the array. Since there are more than 1 thread executing, while one thread is iterating the array, another could be calling addObject. Thus generating your crash of 'collection was mutated while being iterating'
Two options for solutions
1) in containsObject, check against a copy of the array rather than the array itself (this is a bad approach because it requires duplicating of the content of the array, BUT it is easy to put in, and for small array sizes, could be a quick fix)
if (![self.objectsArray containsObject: objectCut]) =>
if (![[NSArray arrayWithArray:self.objectsArray] containsObject: objectCut])
Again, this should stop your sporadic crashes, but this is a BAD SOLUTION (because of the memory duplication)
2) create a temporary array to add the objects to, and once all threads are finished, add the contents of the temporary array to self.objectsArray

The error says the reason of crash. It is not allowed to change an array while enumerating.
if (![self.objectsArray containsObject: objectCut])
{
[self.objectsArray addObject: objectCut];
}
In the above code you were enumerating the array an inserting and object. This is where the error will be given. Hope this helps .. :)

Related

What happens when NSTimer kicks in

I have a iOS construction where I get callbacks from an underlying class.
This callback make changes to a NSMutablearray.
At the same time I have a NSTimer that makes a callback to a method that makes changes to the same NSMutable array.
I see a potential problem here if the callbacks "collide" working with the NSMutablearray.
I am not sure how to deal with this. Could NSLock do the trick or should I instantiate my NSMutablearray as atomic?
You should make sure that any change to the mutable array occurs on the same thread. This will make sure there can be no 'collisions'. If your timer fires on the main thread, and your callback also occurs on the main thread, everything is good.
If the timer and the callback are on different threads, you can serialize the access to the array using a serial GCD-queue. When you do this, ANY AND ALL access to this array should be done on this queue (keep a reference to this queue in a property for instance).
NSLock might help you, but if you are working on the main thread, this is usually not a good idea, as you might be blocking the main queu, which affects user-interaction / scrolling behviour.
Also, atomic only means that getting or setting the pointer to the array is thread safe, i.e.: a valid value will be returned or set (dors not mean it will be the correct value though). Any operations you do on it have nothing to do with the property being atomic or nonatomox.

-allKeys on background thread results in error: __NSDictionaryM was mutated while being enumerated

I've come across an interesting issue using mutable dictionaries on background threads.
Currently, I am downloading data in chunks on one thread, adding it to a data set, and processing it on another background thread. The overall design works for the most part aside from one issue: On occasion, a function call to an inner dictionary within the main data set causes the following crash:
*** Collection <__NSDictionaryM: 0x13000a190> was mutated while being enumerated.
I know this is a fairly common crash to have, but the strange part is that it's not crashing in a loop on this collection. Instead, the exception breakpoint in Xcode is stopping on the following line:
NSArray *tempKeys = [temp allKeys];
This leads me to believe that one thread is adding items to this collection while the NSMutableDictionary's internal function call to -allKeys is enumerating over the keys in order to return the array on another thread.
My question is: Is this what's happening? If so, what would be the best way to avoid this?
Here's the gist of what I'm doing:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
for (NSString *key in [[queue allKeys] reverseObjectEnumerator]) { //To prevent crashes
NEXActivityMap *temp = queue[key];
NSArray *tempKeys = [temp allKeys]; //<= CRASHES HERE
if (tempKeys.count > 0) {
//Do other stuff
}
}
});
You can use #synchronize. And it will work. But this is mixing up two different ideas:
Threads have been around for many years. A new thread opens a new control flow. Code in different threads are running potentially concurrently causing conflicts as you had. To prevent this conflicts you have to use locks like #synchronized do.
GCD is the more modern concept. GCD runs "on top of threads" that means, it uses threads, but this is transparent for you. You do not have to care about this. Code running in different queues are running potentially concurrently causing conflicts. To prevent this conflicts you have to use one queue for shared resources.
You are already using GCD, what is a good idea:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
The same code with threads would look like this:
[[NSThread mainThread] performSelector:…];
So, using GCD, you should use GCD to prevent the conflicts. What you are doing is to use GCD wrongly and then "repair" that with locks.
Simply put all accesses to the shared resource (in your case the mutable dictionary referred by temp) into on serial queue.
Create a queue at the beginning for the accesses. This is a one-timer.
You can use one of the existing queues as you do in your code, but you have to use a serial one! But this potentially leads to long queues with waiting tasks (in your example blocks). Different tasks in a serial queue are executed one after each other, even there are cpu cores idle. So it is no good idea to put too many tasks into one queue. Create a queue for any shared resource or "subsystem":
dispatch_queue_t tempQueue;
tempQueue = dispatch_queue_create("tempQueue", NULL);
When code wants to access the mutable dictionary, put it in a queue:
It looks like this:
dispatch_sync( tempQueue, // or async, if it is possible
^{
[tempQueue setObject:… forKey:…]; // Or what you want to do.
}
You have to put every code accessing the shared resource in the queue as you have to put every code accessing the shared resource inn locks when using threads.
From Apple documentation "Thread safety summary":
Mutable objects are generally not thread-safe. To use mutable objects
in a threaded application, the application must synchronize access to
them using locks. (For more information, see Atomic Operations). In
general, the collection classes (for example, NSMutableArray,
NSMutableDictionary) are not thread-safe when mutations are concerned.
That is, if one or more threads are changing the same array, problems
can occur. You must lock around spots where reads and writes occur to
assure thread safety.
In your case, following scenario happens. From one thread, you add elements into dictionary. In another thread, you accessing allKeys method. While this methods copies all keys into array, other methods adds new key. This causes exception.
To avoid that, you have several options.
Because you are using dispatch queues, preferred way is to put all code, that access same mutable dictionary instance, into private serial dispatch queue.
Second option is passing immutable dictionary copy to other thread. In this case, no matter what happen in first thread with original dictionary, data still will be consistent. Note that you will probably need deep copy, cause you use dictionary/arrays hierarchy.
Alternatively you can wrap all points, where you access collections, with locks. Using #synchronized also implicitly create recursive lock for you.
How about wrapping where you get the keys AND where you set the keys, with #synchronize?
Example:
- (void)myMethod:(id)anObj
{
#synchronized(anObj)
{
// Everything between the braces is protected by the #synchronized directive.
}
}

NSMutableArray Thread Concurrency with GCD

I have an NSMutableArray in a "sharedStore"-pattern singleton.
Publicly, it's accessible only through methods that cast it as an NSArray. Within the class, it's
#property (nonatomic, copy) NSMutableArray *myItems;
This array never gets manipulated outsdie the singleton but ViewControllers send the singleton messages to manipulate this controller. Some of these messages empty the array, some re-populate it, etc.
Having ended up in a situation where the array was empty in one method call and not yet empty in the next, I've started implementing some concurrency behaviour.
Here's what I'm doing so far:
In the .m file of the singleton, I have a
#property (nonatomic, strong) dispatch_queue_t arrayAccessQueue;
In my singleton's initializer it gets created as a serial queue. And then, every method that has anything to do with mutating this array does so from within a dispatch_sync call, for example:
dispatch_sync(self.arrayAccessQueue, ^{
[_myItems removeAllObjects];
});
This has made things better and has made my app behave more smoothly. However, I have no way of quantifying that beyond it having fixed that one odd behaviour described above. I also kind of feel like I'm in the dark as to any problems that may be lurking beneath the surface.
This pattern makes sense to me, but should I be using something else, like #synchronize or NSLock or NSOperationQueue? Will this come back to bite me?
Using dispatch_sync is fine as long as you wrap all array reads and writes and you ensure it is a serial queue.
But you could improve things by allowing concurrent reads. To do this, use dispatch_sync around all array reads and use dispatch_barrier_sync around all array writes. And setup the queue to be concurrent.
Do this ensures only a single write can happen at a time, reads will be block until the write is done, and a write will wait until all current reads are done.
Using a GCD queue concurrent and providing sort of accessor to your array you can synchronize reading and writing by using dispatch_sync while reading and dispatch_barrier_async while writing.
- (id)methodToRead {
id __block obj = nil;
dispatch_sync(syncQueue, ^{
obj = <#read_Something#>;
});
return obj;
}
- (void) methodsForWriting:(id)obj {
dispatch_barrier_async(syncQueue, ^{
// write passing obj to something
});
}
This will guarantee that during writing everything is locked from reading.
Using GCD is the right choice. The only "gotcha" is that you need to do ALL operations on that queue: add, remove, insert, etc.
I will also mention you need to ensure that you do not use a concurrent queue. You should be using a serial queue, which is the default anyways.

enumerateKeysAndObjectsUsingBlock: can I be sure it is called on the same thread?

Lately I am using enumerateKeysAndObjectsUsingBlock: and today I have hesitated when one of my colleague at work place pointed out that this enumeration method can be called from separate thread and my code may not work as expected. He even suggested I should use fast enumeration. Problem is I really like enumerateKeysAndObjectsUsingBlock: and dislike fast enumeration due to its nature when it comes to dictionary.
my method looked as following
- (NSArray *)someMethod
{
__block NSMutableArray *myArray = [NSMutableArray array];
[self.myDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[myArray addObject:obj];
}
return myArray;
}
Can I be sure that myArray will always return the expected values (assuming self.myDictionary is not empty) and it will be always called on the same thread as the someMethod?
I am aware there is a method enumerateKeysAndObjectsWithOptions:usingBlock: and calling it with NSEnumerationConcurrent option will run enumeration simultaneously on multiple threads.
But I can't find any documentation regarding enumerateKeysAndObjectsUsingBlock.
The same refers to enumerateObjectsUsingBlock: used on array.
As you observe, unless you call enumerateKeysAndObjectsWithOptions with the NSEnumerationConcurrent option, you can be assured that they won't be performed concurrently. As to whether the non-concurrent rendition of enumerateKeysAndObjects runs on the same thread or not (though, I'd wager it does), it doesn't really matter if it didn't run on the same queue because this is run synchronously with respect to the thread that you called the enumeration method. Bottom line, your coworker's thread-related concerns are without merit.
I would note, though, that in my anecdotal testing, these enumeration methods are slower than fast enumeration. If speed is of paramount interest (e.g. especially if using huge dictionaries/arrays), then you might want to employ fast enumeration. In most scenarios, though, the difference is not material.

Passing multiple parameters into function using array

I have to maintain a code of somebody. The code opens a thread. The thread worker function in iOS (and any other language I know of) accepts only one parameter. In order to overcome this, the code creates an array, adds all the parameters into an array and passes it into the thread. Here is the code.
NSArray* params = [[NSArray alloc] initWithObjects: mainView, actionFlag, nil];
[self performSelectorOnMainThread:#selector(updateWorker:) withObject:params waitUntilDone:NO];
And the function is called this way
-(void)updateWorker:(NSArray*)params
{
UIView* view = [params objectAtIndex:0];
bool actionFlag = ((NSNumber*)[params objectAtIndex:1]).boolValue;
/* do stuff with view and actionFlag */
}
I have a gut feeling that this is very wrong at so many levels but cannot built a valid argument for this case.
What are the drawbacks of passing number of arguments as an array?
Actually what you are doing is technically correct (but I do understand why it feels wrong).
If you want to feel better, what I would do in this case is instantiate (create) a "NSDictionary" object and then set the objects / values to useful keys and in your "updateWorker" method, fetch the objects via "objectForKey:".
Doing it this way will be easier for you (or somebody else) to maintain in the future, as you won't have to poke around to see what goes in array position 1, array position 2, etc.
Most of them are future updates,
Some cases (not so rare) may happen:
Addition of new parameters to the array
changing the order of elements in the array
removing elements in the array
problems when releasing and retaining elements in the array (not ARC)
One point to note here, is that all of these cases will be hard to debug, since you will be moving from one thread to the other

Resources