This blog post covers the major differences. In summary:
- Fast enumeration is available on OS X 10.5+, blocks are available on 10.6+
- For simple enumeration, fast enumeration is a bit faster than block-based enumeration
- It's easier (and more performant) to do concurrent or reverse enumeration with block-based enumeration than with fast enumeration
- When enumerating over an
NSDictionary
you can get key and value in one hit with a block-based enumerator, whereas with fast enumeration you have to use the key to retrieve the value in a separate message send.
Regarding the last point (NSDictionary enumeration), compare this:
for (id key in dictionary)
{
id obj = [dictionary objectForKey: key];
// do something with key and obj
}
to this:
[dictionary enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop) {
// do something with key and obj
}];
In addition, both methods protect mutable collections from mutation inside the enumeration loop. Interestingly, if you try to mutate the collection inside a block-based enumeration, you get an exception thrown by CoreFoundation's __NSFastEnumerationMutationHandler
, suggesting that there's some common code under the hood.
NSMutableArray *myArray = [NSMutableArray arrayWithObjects:@"a", @"b", nil];
[myArray enumerateObjectsUsingBlock:^(id anObject, NSUInteger idx, BOOL *stop) {
// Attempt to mutate the array during enumeration
[myArray addObject:@"c"];
}];
Output:
2011-12-14 22:37:53.716 Untitled[5809:707] *** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSArrayM: 0x109614190> was mutated while being enumerated.'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff8cca7286 __exceptionPreprocess + 198
1 libobjc.A.dylib 0x00007fff8319ad5e objc_exception_throw + 43
2 CoreFoundation 0x00007fff8cd311dc __NSFastEnumerationMutationHandler + 172
3 CoreFoundation 0x00007fff8cc9efb4 __NSArrayEnumerate + 612
4 Untitled 0x00000001094efcea main + 250
5 Untitled 0x00000001094efbe4 start + 52
6 ??? 0x0000000000000001 0x0 + 1
)
terminate called throwing an exceptionRun Command: line 1: 5809 Abort trap: 6 ./"$2"