Give this code
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(@"----");
});
I'm expecting that this code crash because at some point the block dispatched on queue2
get executed concurrently to the enumeration and this will trigger the assertion that you cannot mutate an the array while enumerating. Indeed, this is what happens.
The interesting part is when you substitute for ( in )
with 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(@"----");
});
In all my different test the block that remove the object is executed in the middle of the enumeration (I see the print of @"----") and the interesting thing is that the enumeration behave correctly printing [8999] 8999
and then [9000] 9001
.
In this case the array is mutated during the enumeration without firing any assertion. Is this an intended behaviour? If yes, why? I'm I missing something?