8

下面是我的代码。

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异常呢?

4

6 回答 6

14

因为你的逻辑有问题。不允许在枚举期间改变集合。在后一种情况下, NSMutableArray 不知道您正在尝试枚举它,仅在第一种情况下。然后它抱怨,因为这是一个语义错误。您通常应该通过可变复制数组和变异副本来解决这些问题,然后用更新的副本替换原始副本。

于 2013-01-22T11:23:25.640 回答
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];

另一种方法是成为另一个集合中的所有项目并稍后将其删除。这样写起来也会更简单。

于 2013-01-22T11:21:23.503 回答
3

快速枚举使集合不可变。因此,每当您使用for(.. in ..)orblock并尝试进行变异时,您都会收到此错误。

每当您需要在循环中更新集合时,您需要采用可变集合并使用常规的 for(;;)、while 或 dowhile 循环。for( .. in ..)使您的可变集合在运行时不可变。

于 2013-01-22T11:21:18.720 回答
3

使用快速枚举时不能修改集合。我写了一篇关于在枚举时进行修改的帖子,这应该会有所帮助。

想再看看它,我从来没有在枚举时谈论过替换。您仍然可以保存索引和对象并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];
于 2013-01-22T11:27:27.233 回答
2

只需添加中断;从您的阵列中删除您的项目后。

    for (NSDictionary *dic in ticketsToBuyArray) {
    if ([[[dic objectForKey:@"ticket_id"]stringValue] isEqualToString:[[ticketDictionary objectForKey:@"ticket_id"]stringValue]])
    {
        [ticketsToBuyArray removeObject:dic];
        break;
    }
}
于 2014-08-02T15:13:03.267 回答
0

我对objective-c没有太多经验,但它通常是枚举器类的一个特性,可以防止在枚举迭代之间更改可枚举对象。

枚举器类在从集合中返回下一个实例之前检查可枚举对象是否执行了变异方法,例如添加、删除或替换。而 foreach 循环通常是基于枚举器和可枚举类(或接口)实现的。

对数组项的索引访问是原生数组方法,一种基本操作,不涉及任何额外的机制。

于 2013-01-22T11:31:34.853 回答