Q:

Remove object in array enumeration

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

sometimes it worked,and sometimes it crashed,why?

A:

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.

