I'm looking for the standard idiom to iterate over an NSArray. My code needs to be suitable for OS X 10.4+.
For OS X 10.4.x and previous:
int i;
for (i = 0; i < [myArray count]; i++) {
id myArrayElement = [myArray objectAtIndex:i];
...do something useful with myArrayElement
}
For OS X 10.5.x (or iPhone) and beyond:
for (id myArrayElement in myArray) {
...do something useful with myArrayElement
}
The standard idiom for pre-10.5 is to use an NSEnumerator and a while loop, like so:
NSEnumerator *e = [array objectEnumerator];
id object;
while (object = [e nextObject]) {
// do something with object
}
I recommend keeping it simple. Tying yourself to an array type is inflexible, and the purported speed increase of using -objectAtIndex:
is insignificant to the improvement with fast enumeration on 10.5+ anyway. (Fast enumeration actually uses pointer arithmetic on the underlying data structure, and removes most of the method call overhead.) Premature optimization is never a good idea — it results in messier code to solve a problem that isn't your bottleneck anyway.
When using -objectEnumerator
, you very easily change to another enumerable collection (like an NSSet, keys in an NSDictionary, etc.), or even switch to -reverseObjectEnumerator
to enumerate an array backwards, all with no other code changes. If the iteration code is in a method, you could even pass in any NSEnumerator and the code doesn't even have to care about what it's iterating. Further, an NSEnumerator (at least those provided by Apple code) retains the collection it's enumerating as long as there are more objects, so you don't have to worry about how long an autoreleased object will exist.
Perhaps the biggest thing an NSEnumerator (or fast enumeration) protects you from is having a mutable collection (array or otherwise) change underneath you without your knowledge while you're enumerating it. If you access the objects by index, you can run into strange exceptions or off-by-one errors (often long after the problem has occurred) that can be horrific to debug. Enumeration using one of the standard idioms has a "fail-fast" behavior, so the problem (caused by incorrect code) will manifest itself immediately when you try to access the next object after the mutation has occurred. As programs get more complex and multi-threaded, or even depend on something that third-party code may modify, fragile enumeration code becomes increasingly problematic. Encapsulation and abstraction FTW! :-)