Enumerating Collections in Objective-C
One question I often get asked is what’s the best way to enumerate the contents of a collection in Objective-C. I thought I would list them all in this blog post as a reference.
There are four different ways to enumerate a collection in Objective-C. They are listed below in the order I recommend them.
1. Block-Based Enumeration #
Block-based enumeration provides the fastest way to enumerate a collection. Here is an example showing how to enumerate an NSArray:
[array enumerateObjectsUsingBlock:
^(id object, NSUInteger index, BOOL *stop) {
if ([self stopEnumerating]) {
*stop = YES;
}
}];
2. Fast Enumeration #
for (id item in collection) {
// ...
}
3. NSEnumerator #
for (id item in [array reverseObjectEnumerator]) {
// ...
}
4. C for Loop #
int count = [array count];
for (int index = 0; index < count; index++) {
id item = [array objectAtIndex:index];
// ...
}
An Important Gotcha #
Although I recommend that you use the block-based enumeration in almost all cases, there is one scenario where you should avoid it. To understand what that scenario could be, let’s look at the code snippet listed below.
- (NSArray *)articlesFromJson:(NSArray *)json
error:(NSError **)error
{
[json enumerateObjectsUsingBlock:
^(NSDictionary *articleJson, NSUInteger index, BOOL *stop)
{
if ([self isJsonInvalid:articleJson
error:error])
{
*stop = YES;
}
}];
}
The articlesFromJson:error:
method accepts a pointer to a pointer to an NSError object as a parameter. That error
parameter is implictly marked as __autorelease
by Clang. Since the enumerateObjectsUsingBlock:
method automatically creates an autorelease pool, the error parameter will get destroyed by Objective-C runtime as soon as the enumeration block is done. As a result, if any other code attempts to access the error parameter after the enumeration
is complete, the app will crash because the memory location it points to has become invalid. Therefore in situations like this, I recommend that you use one of the other alternatives listed above.