避免“NSArray在被枚举时发生变异”

时间:2012-04-14 21:07:42

标签: objective-c cocoa cocoa-touch collections nsmutablearray

我有一个NSMutableArray存储用于Box2d物理模拟的鼠标接口。当使用多个手指播放时,我会得到例外陈述

  

NSArray在被枚举时被突变

我知道这是因为我正在从数组中删除对象,同时也通过它枚举,使枚举无效。

我想知道的是,解决这个问题的最佳策略是什么?我在网上看到了一些解决方案:@synchronized,在枚举之前复制数组或将触摸联接放入垃圾数组中以便以后删除(我不确定是否会起作用,因为我需要删除鼠标点将数据从世界中移除后直接排列。

10 个答案:

答案 0 :(得分:47)

您始终可以在没有枚举器的情况下进行迭代。 这意味着常规 for 循环,以及删除对象时: - 递减索引变量并继续; 。 如果你在进入for循环之前缓存数组的计数,那么确保在删除对象时也减少它。

无论如何,我不明白为什么带有对象的数组以后删除会有问题。我不知道你所拥有的确切情况和涉及的技术,但理论上应该没有问题。 因为在大多数情况下使用此方法时,您只能在第一次枚举中执行任何操作,并在枚举删除数组时执行实际操作。 如果你有一种情况,在第一次枚举中你正在检查再次对同一个数组并且你需要知道对象不再存在,你可以添加一个检查以查看它们是否存在在删除数组中。

无论如何,希望我帮忙。 祝你好运!

答案 1 :(得分:36)

您可以这样做:

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

答案 2 :(得分:33)

最简单的方法是通过数组枚举向后,这意味着当您删除对象时,下一个索引不会受到影响。

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

答案 3 :(得分:5)

锁定(@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个对象。你可以在这里使用lock,但如果有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
}

答案 4 :(得分:1)

以上所有内容对我都不起作用,但确实如此:

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

注意:这将全部删除。如果您需要选择需要删除的项目,那么这将无效。

答案 5 :(得分:1)

使用for循环而不是枚举是可以的。但是一旦你开始删除数组元素,如果你正在使用线程,要小心。仅仅减少计数器是不够的,因为你可能正在删除错误的东西。 正确的方法之一是创建数组的副本,迭代副本并从原始文件中删除。

edc1591是正确的方法。

[Brendt:在复制时需要从原文中删除]

答案 6 :(得分:1)

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 :(得分:1)

我遇到了同样的问题,解决方法是枚举副本并改变原文,正如edc1591正确提到的那样。我已经尝试了代码,我不再收到错误了。如果您仍然收到错误,则表示您仍然在for循环中改变副本(而不是原始副本)。

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

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

答案 8 :(得分:0)

也许你在枚举它时删除或添加了对象到mutableAray。 在我的情况下,在这种情况下出现错误。

答案 9 :(得分:0)

我遇到过这个错误,在我的情况下,这是因为多线程情况。一个线程枚举一个数组,而另一个线程同时从同一个数组中删除对象。希望这有助于某人。