Coocoo4Cocoa
Coocoo4Cocoa

Reputation: 50916

Objective-C 2.0 and Fast Enumeration throwing exceptions

I have a block of code which is similar to the following:

for (NSDictionary *tmp in aCollection) {
   if ([[bar valueForKey:@"id"] isEqualToString:[tmp valueForKey:@"id"]])
   {
      break;
   }
   else
   {
      [aCollection addObject:bar];
       }
 }

Is this technically an exception in Objective-C 2.0? It appears you cannot mutate a collection with fast enumeration. This is the result of an error:

*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <NSCFArray: 0x396000> was mutated while being enumerated.'

What's the best way to solve this?

Upvotes: 5

Views: 8805

Answers (7)

kalpesh jetani
kalpesh jetani

Reputation: 1803

solved

i has same problem in coredata objects enumeration .

do not change sub object which relies in ARRAY on which Loop is Running

Like, if i changed/Modify object while On loop .. it will give this error

 for (LoadList *objLL in ArrLoadList) {             // here Enumaration is Going on
        // here i has removed objects in ArrLoadList and reassign the ArrLoadList .. 
        // it will gives me this error 
        // So Don't change the Main Array object 
 }

Upvotes: 0

Jeff Laing
Jeff Laing

Reputation: 923

Everyone else has offered useful answers, all of which seem correct.

Your code, however, does not do what they think it does. It probably doesn't do what you think it does either.

You try to add 'bar' to the collection if the first object returned by the enumerator does not match bar.

Everyone thinks that you are trying to add bar if it doesn't exist in the collection at all.

I suspect you taken a Python construct and ported it incorrectly.

Upvotes: 1

Dan Rosenstark
Dan Rosenstark

Reputation: 69787

Just copy the array. Otherwise you're messing with the collection that you're iterating:

for (NSDictionary *tmp in aCollection.copy) {
  if ([[bar valueForKey:@"id"] isEqualToString:[tmp valueForKey:@"id"]])
     break;
  [aCollection addObject:bar];
}

Upvotes: 2

Alex
Alex

Reputation: 26859

The line

[aCollection addObject:bar]

is your problem. You cannot modify aCollection while enumerating it. The better approach would be to create a temporary NSMutableArray, add bar to that, then call [aCollection addObjectsFromArray:] with your temporary array.

For example:

NSMutableArray *foundObjects = [NSMutableArray array];
for (NSDictionary *aDictionary in aCollection) {
    if ([[bar objectForKey:@"id"] isEqual:[aDictionary objectForKey:@"id"]])
       break;

    [foundObjects addObject:bar];
}
[aCollection addObjectsFromArray:foundObjects];

Upvotes: 6

Andrew Grant
Andrew Grant

Reputation: 58804

Well the way to solve it is not to mutate the array (e.g. add an object) while enumerating it :)

The problem here is that modifying the array by adding/removing elements could cause the enumeration values to become invalid, hence why it's a problem.

In your case The easiest way to solve this is fixing the bug in your code. Your code is doing the "else add" clause for every item in the array and I'm quite sure that's not what you want.

Try this;

bool found = false;
for (NSDictionary *tmp in aCollection)
{
   if ([[bar valueForKey:@"id"] isEqualToString:[tmp valueForKey:@"id"]])
   {
      found = true;
      break;
   }
}

if (!found)
{
 [aCollection addObject:bar];
}

Upvotes: 12

Alex Wayne
Alex Wayne

Reputation: 187262

Make a copy of the collection and iterate through that. Then you can swap out or add to your original collection without issues.

Upvotes: 6

mouviciel
mouviciel

Reputation: 67879

From The Objective-C 2.0 Programming Language, you cannot modify the collection being enumerated:

Enumeration is “safe”—the enumerator has a mutation guard so that if you attempt to modify the collection during enumeration, an exception is raised.

Upvotes: 3

Related Questions