我有一个NSMutableArray
,它存储了Box2d物理仿真的鼠标关节。当使用多个手指玩时,我会收到异常,指出
NSArray在枚举时被更改了
我知道这是因为我正在从数组中删除对象并同时枚举它,从而使enum无效。
我想知道的是,解决这个问题的最佳策略是什么?我在网上看到了一些解决方案:@synchronized
、枚举之前复制数组或将触摸关节放入垃圾数组进行以后的删除(我不确定这是否有效,因为我需要在从世界中删除关节后立即从数组中删除鼠标关节)。
我有一个NSMutableArray
,它存储了Box2d物理仿真的鼠标关节。当使用多个手指玩时,我会收到异常,指出
NSArray在枚举时被更改了
我知道这是因为我正在从数组中删除对象并同时枚举它,从而使enum无效。
我想知道的是,解决这个问题的最佳策略是什么?我在网上看到了一些解决方案:@synchronized
、枚举之前复制数组或将触摸关节放入垃圾数组进行以后的删除(我不确定这是否有效,因为我需要在从世界中删除关节后立即从数组中删除鼠标关节)。
你可以这样做:
NSArray *tempArray = [yourArray copy];
for(id obj in tempArray) {
//It's safe to remove objects from yourArray here.
}
[tempArray release];
tempArray
应该像这样初始化 NSArray *tempArray = [NSArray arrayWithArray:yourArray]
吗?看起来这段代码不会达到 OP 的预期。 - Tim Arnoldfor (int = 0; i < yourArray.count; i++) { /* 在此处删除yourArray中的对象是安全的。*/ }
,这不会使用数组的枚举器。 - Itachi最简单的方法是反向枚举数组,这意味着当您删除一个对象时,下一个索引不会受到影响。
for (NSObject *object in [myMutableArray reverseObjectEnumerator]) {
// it is safe to test and remove the current object
if (AddTestHere) {
[myMutableArray removeObject: object];
}
}
使用锁定操作(@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
}
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];
使用for循环而不是枚举是可以的。但是一旦你开始删除数组元素,如果你正在使用线程,请小心。仅仅递减计数器是不够的,因为你可能会删除错误的内容。其中一种正确的方法是创建数组的副本,迭代副本并从原始数组中删除。
edc1591是正确的方法。
[Brendt:需要在迭代副本时从原始数组中删除]
以上的方法对我都无效,但是这个有效:
while ([myArray count] > 0)
{
[<your delete method call>:[myArray objectAtIndex:0]];
}
我曾经遇到过同样的问题,解决方法是对复制品进行枚举并改变原始数据,就像edc1591所正确提到的那样。我尝试了这段代码,不再出现错误。如果你仍然看到错误,那意味着你在for循环中仍然更改的是复制品而不是原始数据。
NSArray *copyArray = [[NSArray alloc] initWithArray:originalArray];
for (id obj in copyArray) {
// tweak obj as you will
[originalArray setObject:obj forKey:@"kWhateverKey"];
}
setObject:forKey
是字典的方法,而不是数组(此外,它必须是可变版本)。否则,该方法是正确的。 - Nicolas Miari我遇到了这个错误,原因是多线程的情况。一个线程在枚举一个数组,而另一个线程同时从同一个数组中移除对象。希望这能帮助到某些人。