以下是我的代码。
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
异常呢?
答案 0 :(得分:11)
因为你的逻辑是有缺陷的。在枚举期间不允许改变集合。在后一种情况下,NSMutableArray不知道你试图计算它,只是在第一种情况下。然后它抱怨,因为这是一个语义错误。您通常应该通过可变复制数组并改变副本来解决这些问题,然后用更新的副本替换原始数据。
答案 1 :(得分:7)
你不能这样做:
[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];
另一种方法就是成为另一个集合中的所有项目,并在以后删除它们。写起来也会更简单。
答案 2 :(得分:3)
使用快速枚举时无法修改集合。我写了一篇应该有帮助的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];
答案 3 :(得分:2)
快速枚举使集合成为不可变的。因此,每当您使用for(.. in ..)
或block
并尝试改变时,您都会收到此错误。
每当你需要在循环中更新集合时,你需要采用可变集合并使用常规的(;;),while或dowhile循环。 for( .. in ..)
使你的可变集合在运行时变为不可变。
答案 4 :(得分:2)
添加休息;从阵列中删除项目后。
for (NSDictionary *dic in ticketsToBuyArray) {
if ([[[dic objectForKey:@"ticket_id"]stringValue] isEqualToString:[[ticketDictionary objectForKey:@"ticket_id"]stringValue]])
{
[ticketsToBuyArray removeObject:dic];
break;
}
}
答案 5 :(得分:0)
我对objective-c没有太多经验,但它通常是枚举器类的一个特性,它可以防止在枚举迭代之间更改可枚举对象。
枚举器类检查可枚举对象是否已执行变异方法,如在从集合中返回下一个实例之前添加,删除或替换。 foreach循环通常基于枚举器和可枚举类(或接口)实现。
对数组项的索引访问是本机数组方法,这是一种基本操作,不涉及任何其他机制。