Безопасный способ перебора массива или словаря и удаления записей? - PullRequest
1 голос
/ 07 мая 2010

Я слышал, что делать что-то подобное - плохая идея. Но я уверен, что есть некоторое эмпирическое правило, которое может помочь понять это правильно.

Когда я часто перебираю NSMutableDictionary или NSMutableArray, мне нужно избавиться от записей. Типичный случай: вы перебираете его и сравниваете запись с чем-то. Иногда результат «больше не нужен», и вы должны удалить его. Но это влияет на индекс всех строк, не так ли?

Так, как я мог безопасно перебрать его, не превысив случайно границы и не перепрыгнув элемент, который не был проверен?

Ответы [ 5 ]

8 голосов
/ 07 мая 2010

Вы делаете это путем создания временного массива или словаря.Либо

  • (Словарь) Добавьте ключи, которые вы хотите позже удалить, во временный массив, либо
  • (Массив) Добавьте индексы, которые вы хотите удалить позже, в NSIndexSet или
  • Добавьте нужные объекты keep во временный массив или словарь

Затем выполните итерацию по временному TODELETE-списку, удалив из основного списка (тамможет быть полезными методами для этого), или замените существующий диктонар / массив вашим "временным".

4 голосов
/ 07 мая 2010

Если вы удалите объект из изменяемого массива при перечислении того же массива, это приведет к сбою приложения. Отслеживайте объекты, которые вы хотите удалить (вы можете хранить их индексы в NSIndexSet) и использовать метод -(void)removeObjectsAtIndexes:(NSIndexSet *)indexes, как только вы выйдете из цикла перечисления. Или сохраните NSArray перечисляемых вами объектов и используйте -(void)removeObjectsInArray:(NSArray *)otherArray, чтобы удалить их после завершения цикла.

Проверьте NSMutableArray документы для объяснения методов.

http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html

2 голосов
/ 12 сентября 2010

Или вы всегда можете прибегнуть к использованию явного индекса в цикле, а затем манипулировать им при удалении объектов. Так это будет выглядеть примерно так

for(unsigned int i = 0; i < [myArray count]; i++) {
      id obj = [myArray objectAtIndex:i];
      if (condition) {
           [myArray removeObject: obj];
           i--;
      }
}
1 голос
/ 13 сентября 2010

Учитывая, что в вашем массиве нет элементов [NSNull null] (что, скорее всего, в любом случае не будет), вы можете просто заменить все элементы, которые нужно удалить, на [NSNull null] во время перечисления, а после завершения перечисления просто вызвать [array removeObjectIdenticalTo:[NSNull null]] ([NSNull null] - это хакер синглтона и фреймворка, позволяющий вставлять nil в массивы).

Это может быть удобно, когда проблема с памятью, и, следовательно, копирование массива не вариант или когда массив просто не может быть заменен копией.

1 голос
/ 12 мая 2010

Вот умный способ создания копии, итерации и удаления:

for (id obj in [[myArray copy] autorelease]) {
    BOOL condition = ...
    if (condition) {
        [myArray removeObject:obj];
    }
}

Если по какой-то причине важно выполнить итерацию в обратном порядке:

for (id obj in [[[myArray copy] autorelease] reverseObjectEnumerator]) {
    BOOL condition = ...
    if (condition) {
        [myArray removeObject:obj];
    }
}
...