1
[array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    if (condition) {
        [array removeObject:obj];
    }
}];

sometimes it worked,and sometimes it crashed,why?

  • http://stackoverflow.com/questions/14457369/collection-was-mutated-while-being-enumerated-error-in-objective-c – nynohu Mar 27 '17 at 07:15
  • 1
    You should probably be using `filteredArrayUsingPredicate` for this instead. As to why: http://stackoverflow.com/questions/9465709/app-crash-during-array-enumerating – luk2302 Mar 27 '17 at 07:16
  • Simple, you shouldnt change the array order/value during enumerate it, if you do, it will affect the looping value also, make a copy before doing this – Tj3n Mar 27 '17 at 07:16
  • @cleverIdiot - the previous comments give you the answer. Unfortunately the answer you have currently accepted does not, it is wrong. You might wish to invite one of the commentators to add an answer so you can accept it. – CRD Mar 27 '17 at 09:02
  • but i need to remove it and reload my table view – cleverIdiot Mar 27 '17 at 11:09
  • @luk2302 - could you expand your comment into an answer, so there is a good one for the OP to accept? – CRD Mar 27 '17 at 16:01

3 Answers3

1

Imagine how you would write the code for -enumerateObjectsUsingBlock: if you were working at Apple and asked to add this. It would probably look something like:

-(void) enumerateObjectsUsingBlock: (void(^)(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop))myBlock
{
    NSUInteger numItems = [self count];
    BOOL       stop = false;
    for( NSUInteger x = 0; x < numItems && stop == false; x++ )
    {
        myBlock( [self objectAtIndex: x], x, &stop );
    }
}

Now if your block calls removeObject:, that means that the array:

0: A
1: B
2: C
3: D

After myBlock( A, 0 ) changes to:

0: B
1: C
2: D

Now x++ gets executed, so next call is myBlock( C, 1 ) -- already you see that the 'B' is now skipped, and the item originally at index 2 is deleted second (instead of the one at index 1). Once we have deleted that, we loop again and the array looks like this:

0: B
1: D

So when -enumerateObjectsUsingBlock: tries to delete the item at index 2, it runs off the end of the array, and you get a crash.

In short, the documentation for -enumerateObjectsUsingBlock: doesn't say anywhere that you may modify the array while you're iterating it, and there is no way for the block to tell the looping code in -enumerateObjectsUsingBlock: that it just deleted an object, so you can't rely on that working.

(You can try this out yourself ... rename this version of enumerateObjectsUsingBlock: to myEnumerateObjectsUsingBlock:, declare it in a category on NSArray, and step through it in the debugger with a program like: [myArray myEnumerateObjectsUsingBlock: ^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop){ [myArray removeObject: obj]; }];)

If you expect you'll be deleting items from the array, there are several workarounds. One, you can make a copy of the array, loop over that, and delete objects from the original array. Another option is to iterate backwards over the array, which means that the indexes of earlier items won't change (try modifying the version of -enumerateObjectsUsingBlock: above and watch what happens in the debugger).

Yet another approach is to write your own method that filters the array. You give it a block that is expected to return YES if you are to keep the object, NO if you shouldn't. Then you loop over the original array, call the block on each item, and create a new array, to which you add all objects for which the block returns YES.

uliwitness
  • 8,532
  • 36
  • 58
0

This is unsafe way. I don't know well internal execution of this language, btw I think when you remove the object, then the size of array decreases, and it would be error when you reach the last element of array. I think in enumeration block, you are not allowed to change array. it is same issue on other languages. You can get further information in this url. http://ronnqvi.st/modifying-while-enumerating-done-right/

Nick Jang
  • 239
  • 4
  • 3
-1

After deletion, your objects are not proper. so make copy as:

[array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    if (condition) {
  int counter =  [array indexOfObject:obj];
    [array removeObjectAtIndex:counter];
    }
}];
Ajjjjjjjj
  • 669
  • 4
  • 12
  • you are still removing objects from an array while enumerating it - that is wrong, please delete. – luk2302 Mar 28 '17 at 10:34