【问题标题】:Objective-C NSMutableArray mutated while being enumerated?Objective-C NSMutableArray 在枚举时发生了变异?
【发布时间】:2012-02-08 16:19:26
【问题描述】:

我偶然发现了一个错误,您尝试从 NSMutableArray 中删除对象,而其他对象正在其他地方添加到其中。为了简单起见,我不知道如何解决它。这是我正在做的事情:

我有 4 个计时器调用 4 个不同的方法,将对象添加到同一个数组中。现在,当我按下某个按钮时,我需要删除数组中的所有对象(或至少一些)。所以我试图首先使所有 4 个定时器无效,然后用数组做我想做的工作,然后启动定时器。我认为这会起作用,因为我不再使用计时器来枚举数组,但似乎它不起作用。

这里有什么建议吗?

【问题讨论】:

  • 这也发生在我身上,但我没有从我的 NSMutableArray 中删除任何内容。然而,我在后台线程中添加项目。它可能每 50 或 100 次仅抛出一次此错误,添加...这是整个应用程序中唯一接触数组的线程...

标签: iphone objective-c xcode


【解决方案1】:

这与您的计时器无关。因为(我假设)您的计时器都在与您的修改方法相同的线程上工作,所以您不需要停止和启动它们。 iOS 不使用中断模型进行定时器回调,它们必须像任何其他事件一样等待轮到他们 :)

你可能正在做类似的事情

for (id object in myArray)
   if (someCondition)
       [myArray removeObject:object];

在遍历可变数组时,您无法对其进行编辑,因此您需要创建一个临时数组来保存要删除的内容

// Find the things to remove
NSMutableArray *toDelete = [NSMutableArray array];
for (id object in myArray)
   if (someCondition)
       [toDelete addObject:object];

// Remove them
[myArray removeObjectsInArray:toDelete];

【讨论】:

  • 我认为最后两行应该是“for (id object in toDelete) [myArray removeObject:object];
  • @TobiasKlüpfel - 你是绝对正确的 - 我已经编辑了我的答案。
  • 只需使用-removeObjectsInArray:。此外,如果通过索引访问,您可以在对其进行迭代时进行修改。
  • 我更喜欢谨慎行事;太多我喜欢的微妙错误!
  • @bluesm 从技术上讲,是的,这是可行的。但是,在我所在的任何团队中,它都不会通过同行评审:) 代码看起来非常复杂,我敢打赌那里的某个地方会有一个错误的错误!除非您从中获得重要的速度/内存增加,否则我每次都会寻求更清晰的代码。
【解决方案2】:

你可以这样做:

for (id object in [myArray copy])
   if (someCondition)
       [myArray removeObject:object];

就像@deanWombourne 说的那样,“你不能在遍历可变数组时编辑它”,所以我在这里做的是创建原始数组的自动发布副本来枚举对象,所以你可以安全地删除您想要的任何内容。

更清晰,更少的锅炉代码(我认为!)。

更新:删除自动释放调用,因为这是 ARC 之前的旧答案。

【讨论】:

  • 非常好 :) 如果您的 myArray 非常大并且您只删除了一些项目,但我当然喜欢它的优雅,这可能是一个性能问题!
  • 这里最优雅,当然 ARC 不需要 autorelease
  • 除了前面提到的性能问题@deanWombourne,每次迭代使用[myArray copy] 可能会减慢您的程序。我强烈建议在 for 循环之前创建一个 NSArray 实例(例如,NSArray *tempArray = [myArray copy];)并改用它。
  • 嗨@pxpgraphics - 如果您正在执行for(n=0;n<myArray.copy.count;++n) { ... } 样式循环,那么您是正确的。然而,快速迭代不是这样工作的——它会枚举数组中的所有项目一次,然后传递它们——(一篇好文章是[这里][nshipster.com/enumerators/])。因此,在这种情况下,您的优化不会加快速度。不过,它可能会使代码更具可读性,这总是一件好事。
  • 很高兴知道@deanWombourne!干杯!
【解决方案3】:

您可以使用 @synchronized() 指令在对数组进行变异时锁定它:

if (someCondition) {
    @synchronized(yourArray) {
        [yourArray removeObject:someObject];
    }
}

更多信息http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocThreading.html

【讨论】:

    【解决方案4】:

    你可以试试:

    for (id object in [myArray reverseObjectEnumerator])
    if (someCondition)
       [myArray removeObject:object];
    

    如果您删除索引 x 处的对象 => 索引 x + 1、x + 2.... 处的对象的索引将被更改。 因此,当您使用 reverseObjectEnumerator 时,删除某些对象后数组中对象的索引仍然会正确。

    希望对您有所帮助。 (接受的答案是明确的解决方案。)

    【讨论】:

      【解决方案5】:

      虽然以上都是真实的......我会分享我的经验与

      在枚举时发生变异

      我所做的很简单,但完全错误:

      for (id obj  in d.dataDashBoardGraph) {
          [tmpData addObject:[obj valueForKey:[[dataToShow[i] componentsSeparatedByString:@"_"] objectAtIndex:1]]];
      
          ...
      }
      

      即使这引起了mutated being enumerated error。摆脱它:

      for (id obj  in d.dataDashBoardGraph) {
         NSString *data = [dataToShow[i] copy];
         [tmpData addObject:[obj valueForKey:[[data componentsSeparatedByString:@"_"] objectAtIndex:1]]];
      
          ... 
      }
      

      然后它完美地工作了。

      【讨论】:

        【解决方案6】:

        NSMutableArray 在被枚举时不能被改变,你可能会在调用你的操作之前创建一个小的延迟:

         for(id key in resultsDictionary) {
                        if ([key isEqual:whichButtonString]) {
                           // [resultsDictionary removeObjectForKey:whichButtonString];
                            [self performSelector:@selector(removeKeyFromDictionary:) withObject:whichButtonString afterDelay:1.0];
                        }
                    }
        

        然后

        -(void) removeKeyFromDictionary : (NSString *) incomingString {
        [resultsDictionary removeObjectForKey:incomingString];
        

        }

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-05-11
          • 1970-01-01
          • 2017-11-22
          • 2015-02-01
          • 2014-02-15
          • 2013-01-29
          • 2017-08-15
          • 2016-11-20
          相关资源
          最近更新 更多