0

For example:

NSMutableArray *array = [NSMutableArray arrayWithObjects:@"A", @"B", @"C", nil];
[array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    if ([obj isEqualToString:@"A"]) {
        [array removeObject:obj];
    }
}];
NSLog(@"%@", array);

The console print B,C.

However:

NSMutableArray *array = [NSMutableArray arrayWithObjects:@"A", @"B", @"C", nil];
[array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    if ([obj isEqualToString:@"A"]) {
        [array removeObject:obj];
    } else if ([obj isEqualToString:@"B"]) {
        [array removeObject:obj];
    }
}];
NSLog(@"%@", array);

The console print B,C.

Why 'B,C' not 'C'?

What happend when deleting obj in an array while enumerating?

1

5 Answers 5

1

Changing a collection you are iterating is sometimes difficult to understand. There are good reasons to avoid it, even in your case there is a simple solution. (But these solutions break very fast, when you remove other elements of the array.)

There are some generic solutions:

A. Iterate over a copy

NSMutableArray *array = [NSMutableArray arrayWithObjects:@"A", @"B", @"C", nil];
NSArray *iterationArray = [array copy];
[iterationArray enumerateObjectsWithOptions: NSEnumerationReverse usingBlock:
^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  if ([obj isEqualToString:@"A"]) {
    [array removeObject:obj];
  } else if ([obj isEqualToString:@"B"]) {
    [array removeObject:obj];
  }
}];

B. Store objects to remove

NSMutableArray *array = [NSMutableArray arrayWithObjects:@"A", @"B", @"C", nil];
NSMutableArray *itemsToRemove = [NSMutableArray new];
[array enumerateObjectsWithOptions: NSEnumerationReverse usingBlock:
^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  if ([obj isEqualToString:@"A"]) {
    [itemsToRemove addObject:obj];
  } else if ([obj isEqualToString:@"B"]) {
    [itemsToRemove addObject:obj];
  }
}];
[array remveObjects:itemsToRemove];

All these ways to do it including your way may cause unexpected results, because -removeObject: removes all occurrences of the object in the array itself, what means the removal of all objects that are equal to the arg, including objects that are not identical.

C. Remove objects with that indexes

If you are iterating using an index – or can easily build an index – it is more efficient to store the indices and then remove with this one. This gives you the opportunity to differ between equal and identical objects. In your case

NSMutableArray *array = [NSMutableArray arrayWithObjects:@"A", @"B", @"C", nil];
NSMutableIndexSet *itemsToRemove = [NSMutableIndexSet new];
__block NSUInteger index=0;
[array enumerateObjectsWithOptions: NSEnumerationReverse usingBlock:
^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) 
{
  if ([obj isEqualToString:@"A"]) 
  {
    [itemsToRemove addIndex:index];
  } else if ([obj isEqualToString:@"B"]) {
    [itemsToRemove addIndex:index];
  }
  index++;
}];
[array removeObjectsAtIndexes:itemsToRemove];
NSLog(@"%@", array);
Sign up to request clarification or add additional context in comments.

Comments

1

The reason why the code doesn't work is mentioned correctly in the other answers, the indices are modified.

If you want to remove objects while enumerating an array you have to enumerate always backwards.

After removing the item at index 1 index 0 remains unchanged.

NSMutableArray *array = [NSMutableArray arrayWithObjects:@"A", @"B", @"C", nil];
[array enumerateObjectsWithOptions: NSEnumerationReverse usingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    if ([obj isEqualToString:@"A"]) {
        [array removeObject:obj];
    } else if ([obj isEqualToString:@"B"]) {
        [array removeObject:obj];
    }
}];
NSLog(@"%@", array);

Comments

0

In first iteration you will get index zero value that is 'A'. If condition satisfied. Then A is removed. Now second iterations index is One. Object at index one is C. Because you removed 'A' then B moves to Zero Index. So in second iteration it checks C not B. Thats why B is not removed.

Comments

0

Enumerator runs on the objects in order. First time you enter the block obj = array[0] which means obj = @"A". Then array[0] is removed. The next time you enter the block obj = array[1], but the array is now @[@"B", @"C"], so obj = @"C". You never iterate on @"B", so it cannot be removed.

In general alternating arrays while iterating through them is extremely dangerous and not recommended. There are additional ways to remove objects from array.

Comments

0

Possible approach is to iterate an array and store indeces you want to remove

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.