Objective c enumerateObjectsUsingBlock:和for(in)之间的不同行为

Objective c enumerateObjectsUsingBlock:和for(in)之间的不同行为,objective-c,multithreading,nsmutablearray,nsarray,enumeration,Objective C,Multithreading,Nsmutablearray,Nsarray,Enumeration,给出这个代码 NSMutableArray *array = [NSMutableArray new]; for (int i = 0; i < 10000; i++) { [array addObject:@(i)]; } queue1 = dispatch_queue_create("com.test_enumaration.1", DISPATCH_QUEUE_CONCURRENT); queue2 = dispatch_queue_create("com.test_enu

给出这个代码

NSMutableArray *array = [NSMutableArray new];
for (int i = 0; i < 10000; i++) {
    [array addObject:@(i)];
}

queue1 = dispatch_queue_create("com.test_enumaration.1", DISPATCH_QUEUE_CONCURRENT);
queue2 = dispatch_queue_create("com.test_enumaration.2", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue1, ^{
    int idx = 0;
    for (NSNumber *obj in array) {
        NSLog(@"[%d] %@", idx, obj);
        idx++;
    }
});

double delayInSeconds = 0.3;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, queue2, ^(void){
    [array removeObjectAtIndex:9000];
    NSLog(@"----");
});

在所有不同的测试中,删除对象的块在枚举的中间执行(我看到了@的打印)——有趣的是枚举正确地打印了<代码> [8999 ] 8999 <代码>然后<代码> [9000 ] 9001 < /Cord>


在这种情况下,数组在枚举过程中发生变异,而不触发任何断言。这是故意的行为吗?若有,原因为何?我遗漏了什么?

自从引入快速枚举以来,它已成为。。。使枚举快速。大多数枚举实现,如
for(in)
EnumerateObjectsSusingBlock:
,都会在后台使用快速枚举

快速枚举将查看数据的存储方式。在NSMutableArray的情况下,我猜底层数据存储在几个数据块中;一个一万项数组可以实现为100项的100个块,每个块将其100项存储在连续内存中。对某些程序集的分析表明(至少在某些iOS设备上)该类被实现为一个巨大的循环缓冲区。无论哪种方式,枚举列表都可能包含多个连续的对象块。最终,确切的存储机制是不相关的;正是对底层连续存储的访问使得快速枚举比其他方法更好

通常,枚举应该防止列表发生变异。通过
for(in)
枚举,您将始终看到这一点。显然,
enumerateObjectsUsingBlock:
的一些实现不能可靠地保证列表在枚举期间不会发生变化。我尝试过的设备上出现断言失败。。。但听起来好像有些设备的这种保护被破坏了。我猜在
nsfastnumerationstate
中使用的变异保护并不完整,可能只是监视单个块而不是整个数组

<强>我认为这是一个bug在代码>枚举对象ObjutsSuxBug:>/P>


此外,根据定义,任何可能在此处生成异常的代码都是错误代码:您需要提供一种机制,以防止您自己的代码在另一个线程对数组进行迭代时尝试修改该数组。

链接答案中说明了@特洛伊木马的可能重复“这两种方法都可以防止可变集合在枚举循环内发生变异”在我的例子中,我不是试图更改枚举块内的集合,而是从另一个线程对其进行变异。@LucaBernardi:这两种变体都会因“集合在枚举时发生了变异”而崩溃“当我运行你的代码时异常。我再次运行测试,对于我来说,是已知的崩溃,并且我看到在枚举中间记录了“-----”,事实上输出是[8999 ] 8999,而[9000 ] 9001I得到了与@ Luxabnnalt所描述的相同的输出。事实上,EnumerateObjectsSusingBlock:在枚举过程中数组发生变异时不会抱怨,只是跳过了删除的对象。您是否有任何资源可以证明NSMutableArray正在将其数据存储在块中?显示它是一个循环缓冲区。Mike Ash暗示在中就是这种情况。在您提到的文章中,Bartosz也在对实际实现进行猜测。除此之外,实现本身并不是开源的,苹果保留在不同设备和操作系统版本上更改实现的能力。所以,不,我不知道它是怎么储存的。我将对这篇文章进行编辑以反映这一点。Mike Ash的文章没有对NSMutableArray的内部进行任何说明。事实上,没有任何说明;只是暗示。另外,具体的实现是不相关的。重点是向读者介绍“连续对象的多个块”的概念,我认为这可能是bug的来源,其含义在哪里。我找不到它。如果一个数组中有多个块,那么Bartosz的大部分观察结果都是不可能的。
NSMutableArray *array = [NSMutableArray new];
for (int i = 0; i < 10000; i++) {
    [array addObject:@(i)];
}

queue1 = dispatch_queue_create("com.test_enumaration.1", DISPATCH_QUEUE_CONCURRENT);
queue2 = dispatch_queue_create("com.test_enumaration.2", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue1, ^{
    [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        NSLog(@"[%d] %@",idx, obj);
    }];
});

double delayInSeconds = 0.3;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, queue2, ^(void){
    [array removeObjectAtIndex:9000];
    NSLog(@"----");
});