Getting "NSOrderedSetArrayProxy was mutated while listing" error without mutation

I have two functions:

which returns an array that is filled with a block

- (NSArray *)getArray { NSArray *someValues = @[@0, @42, @23, @5, @8, @2013]; NSArray *filter = @[@42, @23, @5]; //replacing this NSMutableOrderedSet with a NSMutableArray //and return just matched then, resolves the problem. //so the exception has to do something with that set. NSMutableOrderedSet *matched = [[NSMutableOrderedSet alloc] init]; for (id value in someValues) { [filter enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { if ([obj isEqual:value]) [matched addObject:value]; }]; } return [matched array]; } 

and another one that lists the returned array from the first method

 - (void)enumArray:(NSArray *)array { NSEnumerator *enumerator = [array objectEnumerator]; for (id obj in enumerator) { if ([obj isEqual:@42]) [enumerator nextObject]; // <== this line causes the error! } } 

If now I do something like this

 NSArray *array = [foo getArray]; [foo enumArray:array]; 

i will receive an NSGenericException with the following message:

The collection <__ NSOrderedSetArrayProxy: 0x123456> was mutated while enumerated

where the hell something mutated. I do not understand. returning a copy from this array solves the problem, but I still don't understand.

The error has something with NSMutableOrderedSet, if I replace the set with an array, I do not get an exception.

some screenshots, exception thrown

all exceptions breakpointcrash

+4
source share
2 answers

You use quick enumeration when changing an instance of an enumerator.

Basically, this is a big no-no for modifying an object that you quickly enumerate (this form of the for loop you use uses fast enumeration). However, you are using [enumerator nextObject]; to access the next object from the enumerator, but this modifies the enumerator, removing the current object from it. So you use nextObject in a for...in loop that mutates the enumerator.

Quickly overcome this problem by using a while loop instead of a for loop, something like this:

 - (void)enumArray:(NSArray *)array { NSEnumerator *enumerator = [array objectEnumerator]; while ((id obj = [enumerator nextObject])) { if ([obj isEqual:@42]) [enumerator nextObject]; } } 

This should go past the quick enumeration / mutation task. Please note: I don’t know why you want to move the enumerator one step when obj is 42, but I assume that makes sense in the context of the entire code base!

+3
source

The main reason is that you cannot edit / modify a mutable array while it is passing.

So here are two solutions:

1.Please use the @synchronized() directive to lock the array while you mutate it.

 - (void)enumArray:(NSArray *)array { NSEnumerator *enumerator = [array objectEnumerator]; for (id obj in enumerator) { if ([obj isEqual:@42]) { @synchronized(enumerator) { [enumerator nextObject]; // <== this line causes the error! } } } } 

2. Just make a copy of you NSArray and use it

 - (void)enumArray:(NSArray *)array { NSEnumerator *enumerator = [[array copy] objectEnumerator]; for (id obj in enumerator) { if ([obj isEqual:@42]) { [enumerator nextObject]; // <== this line causes the error! } } } 
0
source

Source: https://habr.com/ru/post/1493382/


All Articles