【发布时间】:2014-04-25 15:19:33
【问题描述】:
给出这个代码
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(@"----");
});
我预计这段代码会崩溃,因为在某个时候,在 queue2 上调度的块会与枚举同时执行,这将触发您无法在枚举时改变数组的断言。确实,这就是发生的事情。
有趣的部分是当您将for ( in ) 替换为enumerateObjectsUsingBlock:
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(@"----");
});
在我所有不同的测试中,删除对象的块在枚举中间执行(我看到了@“----”的打印),有趣的是枚举行为正确打印[8999] 8999然后[9000] 9001。
在这种情况下,数组在枚举过程中发生突变,而不触发任何断言。这是预期的行为吗?如果是,为什么?我错过了什么?
【问题讨论】:
-
@trojanfoe 不是真的,在链接的答案中说“这两种方法都可以保护可变集合免受枚举循环内的突变”在我的情况下,我并没有尝试更改枚举块内的集合但我正在从另一个线程改变它。
-
-
我再次运行测试,对我来说知道崩溃,我看到在枚举中间记录了“----”,实际上输出是 [8999] 8999 和 [ 9000] 9001
-
我得到了与@LucaBernardi 描述的相同的输出。实际上,似乎 enumerateObjectsUsingBlock: 在枚举时数组发生突变并简单地跳过删除的对象时不会抱怨。
标签: objective-c multithreading nsmutablearray nsarray enumeration