Подтвердить что ты не робот

Избегая "NSArray был мутирован при перечислении"

У меня есть NSMutableArray, в котором хранятся мышиные точки для моделирования физики Box2d. При использовании более одного пальца для воспроизведения получаются исключения из

NSArray был мутирован при перечислении

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

Что я хочу знать, так это лучшая стратегия для решения этой задачи? Я видел несколько решений в Интернете: @synchronized, копируя массив перед перечислением или помещением касательного сустава в массив мусора для последующего удаления (что я не уверен, что это сработает, потому что мне нужно удалить mousejoint из массива прямо после удаления его из мира).

4b9b3361

Ответ 1

Вы можете всегда выполнять итерацию без перечислителя. Это означает регулярный цикл для, а при удалении объекта: - уменьшите индексную переменную и продолжить;. если вы кешируете счет массива перед вводом for-цикла, тогда убедитесь, что вы уменьшаете его при удалении объекта.

В любом случае, я не понимаю, почему проблема с массивом с объектами для последующего удаления. Я не знаю точной ситуации, которая у вас есть, и технологий, но теоретически не должно быть проблем. Поскольку в большинстве случаев при использовании этого метода вы можете просто ничего не делать в первом перечислении и выполнять настоящую работу при перечислении массива удаления. И если у вас есть ситуация, когда в первом перечислении вы проверяете что-то снова против одного и того же массива, и вам нужно знать, что объектов больше нет, вы можете просто добавить проверку, чтобы увидеть, находятся ли они в массиве удаления.

В любом случае, надеюсь, я помог. Удачи!

Ответ 2

Вы можете сделать что-то вроде этого:

NSArray *tempArray = [yourArray copy];
for(id obj in tempArray) {
    //It safe to remove objects from yourArray here.
}
[tempArray release];

Ответ 3

Самый простой способ - перечислить назад через массив, что означает, что при удалении объекта следующий индекс не будет затронут.

for (NSObject *object in [mutableArray reverseObjectEnumerator]) {
    // it is safe to test and remove the current object      
    if (AddTestHere) {
        [savedRecentFiles removeObject: object];
    }
}

Ответ 4

Операция блокировки (@synchronized) намного быстрее, чем копирование всего массива снова и снова. Конечно, это зависит от того, сколько элементов имеет массив, и как часто оно выполняется. Представьте, что у вас есть 10 потоков, выполняющих этот метод одновременно:
- (void)Callback
{
  [m_mutableArray addObject:[NSNumber numberWithInt:3]];
  //m_mutableArray is instance of NSMutableArray declared somewhere else

  NSArray* tmpArray = [m_mutableArray copy];
  NSInteger sum = 0;
  for (NSNumber* num in tmpArray)
      sum += [num intValue];

  //Do whatever with sum
}

Он копирует n + 1 объектов каждый раз. Вы можете использовать блокировку здесь, но что, если есть 100k элементов для итерации? Массив будет заблокирован до тех пор, пока итерация не будет завершена, а другим потокам придется ждать, пока не будет освобождена блокировка. Я думаю, что копирующий объект здесь более эффективен, но он также зависит от того, насколько большой этот объект и что вы делаете на итерации. Блокировка должна всегда быть кипетом в течение самого короткого периода времени. Поэтому я бы использовал блокировку для чего-то вроде этого.

- (void)Callback
{
  NSInteger sum = 0;
  @synchronized(self)
  {
    if(m_mutableArray.count == 5)
      [m_mutableArray removeObjectAtIndex:4];
    [m_mutableArray insertObject:[NSNumber numberWithInt:3] atIndex:0];

    for (NSNumber* num in tmpArray)
      sum += [num intValue];
  }
  //Do whatever with sum
}

Ответ 5

Использование цикла for вместо перечисления - это нормально. Но как только вы начнете удалять элементы массива, будьте осторожны, если вы используете потоки. Этого недостаточно, чтобы просто уменьшить счетчик, поскольку вы можете удалить неправильные вещи. Одним из правильных способов является создание копии массива, повторная копия и удаление из оригинала.

edc1591 - правильный метод.

[Brendt: нужно удалить из оригинала при повторении через копию]

Ответ 6

for(MyObject *obj in objQueue)
{
//[objQueue removeObject:someof];//NEVER removeObject in a for-in loop
    [self dosomeopearitions];
}

//instead of remove outside of the loop
[objQueue removeAllObjects];

Ответ 7

У меня была та же проблема, и решение состоит в том, чтобы перечислить копию и изменить ее оригинал, как правильно сказано в edc1591. Я пробовал код, и я больше не получаю ошибку. Если вы все еще получаете ошибку, это означает, что вы все еще мутируете копию (вместо оригинала) внутри цикла for.

NSArray *copyArray = [[NSArray alloc] initWithArray:originalArray];

for (id obj in copyArray) { 
    // tweak obj as you will 
    [originalArray setObject:obj forKey:@"kWhateverKey"];
}

Ответ 8

Все вышеизложенное не сработало для меня, но это произошло:

 while ([myArray count] > 0) 
 {
      [<your delete method call>:[myArray objectAtIndex:0]];
 }

Примечание: это удалит все. Если вам нужно выбрать, какие элементы нужно удалить, это не сработает.

Ответ 9

Возможно, вы удалили или добавили объекты в свой mutableAray, перечисляя его. В моей ситуации в этом случае появилась ошибка.

Ответ 10

Я столкнулся с этой ошибкой, и в моем случае это было из-за многопоточной ситуации. Один поток перечислял массив, а другой поток удалял объекты из одного массива одновременно. Надеюсь, это поможет кому-то.