在目标 C 中枚举错误时,集合发生了突变
Posted
技术标签:
【中文标题】在目标 C 中枚举错误时,集合发生了突变【英文标题】:Collection was mutated while being enumerated error in objective C 【发布时间】:2013-01-22 11:19:03 【问题描述】:下面是我的代码。
NSMutableArray *arr = [[NSMutableArray alloc] init];
[arr addObject:@"5"];
[arr addObject:@"7"];
[arr addObject:@"8"];
[arr enumerateObjectsUsingBlock:^(NSString *obj,NSUInteger idx,BOOL *stop)
[arr replaceObjectAtIndex:idx withObject:@"10"];
];
我得到的异常日志
*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSArrayM: 0x742a580> was mutated while being enumerated.'
*** First throw call stack:
(0x1596012 0x12a3e7e 0x161ecc5 0x158fe1b 0x158fa16 0x158f925 0x2ba4 0x1e87b7 0x1e8da7 0x1e9fab 0x1fb315 0x1fc24b 0x1edcf8 0x25f8df9 0x25f8ad0 0x150bbf5 0x150b962 0x153cbb6 0x153bf44 0x153be1b 0x1e97da 0x1eb65c 0x29fd 0x2925)
libc++abi.dylib: terminate called throwing an exception
当我使用 for 循环时,代码运行良好
for (int i = 0 ; i< arr.count; i++)
[arr replaceObjectAtIndex:i withObject:@"8"];
所以当我使用 enumerateObjectsUsingBlock 时,我遇到了异常。但两者都是枚举。对 ?那为什么上面的代码会给出was mutated while being enumerated
异常呢?
【问题讨论】:
根据@H2CO3 的回答,下面的代码解决了这个问题,我认为这将是一个好方法。更新:NSMutableArray *tempArr = [arr mutableCopy]; [tempArr enumerateObjectsUsingBlock:^(NSString *obj,NSUInteger idx,BOOL *stop) [arr replaceObjectAtIndex:idx withObject:@"10"]; ] mutation within fast enumeration? 的可能重复项 【参考方案1】:因为你的逻辑有缺陷。不允许在枚举期间改变集合。在后一种情况下, NSMutableArray 不知道您正在尝试枚举它,仅在第一种情况下。然后它抱怨,因为这是一个语义错误。您通常应该通过可变复制数组和变异副本来解决这些问题,然后用更新的副本替换原始副本。
【讨论】:
所以解决错误 NSMutableArray *tempArr = [arr mutableCopy]; [tempArr enumerateObjectsUsingBlock:^(NSString *obj,NSUInteger idx,BOOL *stop) [arr replaceObjectAtIndex:idx withObject:@"10"]; ];【参考方案2】:你不能这样做:
[arr enumerateObjectsUsingBlock:^(NSString *obj,NSUInteger idx,BOOL *stop)
[arr replaceObjectAtIndex:idx withObject:@"10"];
];
因为您无法在枚举集合时对其进行变异。
我的建议是把所有的操作放到一个操作队列中,枚举完之后执行:
NSOperationQueue* queue= [NSOperationQueue new];
queue.maxConcurrentOperationCount=1;
[queue setSuspended: YES];
[arr enumerateObjectsUsingBlock:^(NSString *obj,NSUInteger idx,BOOL *stop)
NSBlockOperation* op=[NSBlockOperation blockOperationWithBlock: ^ (void)
[arr replaceObjectAtIndex:idx withObject:@"10"];
];
[queue addOperation: op];
];
[queue setSuspended: NO];
[queue waitUntilAllOperationsAreFinished];
另一种方法是成为另一个集合中的所有项目并在以后删除它们。这样写起来也会更简单。
【讨论】:
块很难掌握,至少对我来说,为什么BOOL *
?
@AKV 这在您必须停止枚举时很有用。在这种特定情况下,它不会有用,但如果你想停止你设置*stop= YES
。我没有采用快速的方法,但我相信 OP 会学到一些有用的东西。
一个布尔指针,因为您想更改原始布尔值,而不仅仅是它的副本。否则调用者不会注意到它。【参考方案3】:
快速枚举使集合不可变。因此,每当您使用for(.. in ..)
或block
并尝试进行变异时,您都会收到此错误。
当您需要在循环中更新集合时,您需要获取可变集合并使用常规的 for(;;)、while 或 dowhile 循环。 for( .. in ..)
使您的可变集合在运行时不可变。
【讨论】:
【参考方案4】:使用快速枚举时不能修改集合。我写了一个post about modifying while enumerating,应该会有所帮助。
想了想再看一遍,我从来没有在枚举时谈论过替换。您仍然可以保存索引和对象并在枚举后使用replaceObjectsAtIndexes:withObjects:
。
NSMutableArray *array = // ...
NSMutableIndexSet *indicesForObjectsToReplace = [NSMutableIndexSet new];
NSMutableArray *objectsToReplace = [NSMutableArray new];
[array enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop)
[indicesForObjectsToRemove addIndex:idx];
[objectsToReplace: @"String you are replacing with"];
];
[array replaceObjectsAtIndexes:indicesForObjectsToReplace
withObjects:objectsToReplace];
【讨论】:
那个链接失效了……【参考方案5】:只需添加中断;从您的数组中删除您的项目后。
for (NSDictionary *dic in ticketsToBuyArray)
if ([[[dic objectForKey:@"ticket_id"]stringValue] isEqualToString:[[ticketDictionary objectForKey:@"ticket_id"]stringValue]])
[ticketsToBuyArray removeObject:dic];
break;
【讨论】:
我在从 NSMutableArray 中删除项目时也遇到了类似的问题。通过在循环中添加中断解决了它。 我猜这仅在您搜索要从数组中删除的单个对象时才有效。【参考方案6】:我对objective-c没有太多经验,但它通常是枚举器类的一个特性,可以防止在枚举迭代之间更改可枚举对象。
Enumerator 类在从集合中返回下一个实例之前检查可枚举对象是否执行了变异方法,例如添加、删除或替换。而 foreach 循环通常是基于枚举器和可枚举类(或接口)实现的。
对数组项的索引访问是原生数组方法,一种基本操作,不涉及任何额外机制。
【讨论】:
以上是关于在目标 C 中枚举错误时,集合发生了突变的主要内容,如果未能解决你的问题,请参考以下文章
集合 <__NSSetM: 0x14ea0160> 在枚举时发生了突变