46

我有一个NSMutableArray存储用于 Box2d 物理模拟的鼠标关节。当使用超过一根手指弹奏时,我会得到例外说明

NSArray 在枚举时发生了变异

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

我想知道的是解决这个问题的最佳策略是什么?我在网上看到了一些解决方案:@synchronized,在枚举之前复制数组或将触摸关节放入垃圾数组以供以后删除(我不确定这是否可行,因为我需要在删除后立即从数组中删除鼠标关节它来自世界)。

4

10 回答 10

48

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

无论如何,我不明白为什么带有稍后删除对象的数组会成为问题。我不知道您的确切情况和所涉及的技术,但理论上应该没有问题。因为在大多数情况下,使用此方法时,您可以在第一次枚举中什么都不做,而在枚举删除数组时做真正的工作。如果您在第一次枚举中再次针对同一个数组检查某些内容,并且您需要知道这些对象不再存在,您只需添加一个检查以查看它们是否在删除数组中。

无论如何,希望我有所帮助。祝你好运!

于 2012-04-14T22:06:58.737 回答
38

你可以这样做:

NSArray *tempArray = [yourArray copy];
for(id obj in tempArray) {
    //It's safe to remove objects from yourArray here.
}
[tempArray release];
于 2012-04-14T21:59:12.207 回答
36

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

for (NSObject *object in [myMutableArray reverseObjectEnumerator]) {
    // it is safe to test and remove the current object      
    if (AddTestHere) {
        [myMutableArray removeObject: object];
    }
}
于 2015-07-07T12:58:53.130 回答
6

锁定(@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
}
于 2013-01-22T19:18:00.960 回答
1

以上所有对我都不起作用,但这确实:

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

注意:这将全部删除。如果您需要选择需要删除的项目,那么这将不起作用。

于 2013-01-29T17:01:05.037 回答
1

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

edc1591 是正确的方法。

[Brendt:在遍历副本时需要从原始中删除]

于 2013-04-25T11:02:36.587 回答
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];
于 2014-01-30T19:40:42.537 回答
1

我遇到了同样的问题,解决方案是枚举副本并改变原件,正如 edc1591 正确提到的那样。我已经尝试了代码,但我不再收到错误消息。如果您仍然收到错误,则意味着您仍在 for 循环内更改副本(而不是原始副本)。

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

for (id obj in copyArray) { 
    // tweak obj as you will 
    [originalArray setObject:obj forKey:@"kWhateverKey"];
}
于 2014-04-22T16:04:59.907 回答
0

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

于 2013-12-08T12:20:15.447 回答
0

我遇到了这个错误,就我而言,这是因为多线程情况。一个线程正在枚举一个数组,而另一个线程同时从同一个数组中删除了对象。希望这可以帮助某人。

于 2016-01-07T12:48:28.760 回答