One of the new features of Objective-C 2.0 included with Leopard is fast enumeration. This is essentially new syntax to iterate collections using new for...in syntax:

NSArray *array = [NSArray arrayWithObjects:
        @"One", @"Two", @"Three", @"Four", nil];
 
for (NSString *element in array) {
    NSLog(@"element: %@", element);
}

The best part about this is that you can use this on your own collection classes by implementing the NSFastEnumeration protocol. This is quite similar to Ruby’s Enumerable module. You can loop through integers really easily in Ruby since Ruby’s Range class implements Enumerable:

#!/usr/bin/env ruby

(1..5).each { |i| puts i }

I thought this would be pretty cool to add to Objective-C, but it turned out to be more difficult than I thought.

The ideal way this would work is to have fast enumeration support with NSRange:

for (int i in NSMakeRange(1, 5))
    printf("%d\n", i);

There are two major problems with this. First is that NSRange is a structure, not a class, so it can’t implement NSFastEnumeration. Even if it was a class, you can’t add protocol implementations via a category. So, I decided to create my own range class: DDRange. I also decided to change the semantics from location and length to start and end values. Thus, the following code would print 5 through 10:

for (int i in [DDRange from: 5 to: 10])
    printf("%d\n", i);

The second major problem is that fast enumeration is designed to iterate over Objective-C objects, not primitive types. Trying to compile the above code results in this cryptic error message:

error: selector element does not have a valid object type

Looking at the code that fast enumeration allegedly generates, I’m not sure why we’d get that error. It never actually calls a method on the elements, so I decided to try using casting to get around this.

Looking at NSFastEnumerationState, the itemsPtr is an id \*. There’s no reason why we can’t just cast normal integer values to an id. Of course, we have to manually cast the id back to an int:

for (id _i_ in [DDRange from: 5 to: 10])
{
    int i = (int) _i_;
    printf("%d\n", i);
}

This isn’t quite as elegant as it could be, but it works. You can get and view the the implementation of DDRange from:

http://www.dribin.org/dave/hg/sandbox/ddrange/

I’m not sure how kosher it is to cast from ints to ids and back. As long as no methods get called, it should be okay. But I’m also not sure how the new garbage collection affects this as these ids are not valid objects. In my simple tests I haven’t found any issues, but that doesn’t mean it’s bulletproof.

Given this uncertainty, plus the fact that casts are required, I’m not sure I’ll actually use this in production code. I may file an enhancement bug, since it doesn’t seem like there’s any inherent reason why fast enumeration couldn’t support primitive types. This would require itemsPtr to be a void \*\* (an array of void \*) instead of id \*, but this could even be changed without breaking current code.

UPDATE: There are two critical flaws with DDRange. Problem number one: sizeof(int) does not equal sizeof(id) on 64-bit, as id is a pointer. Thus, I should be using NSInteger and long:

for (id _i_ in [DDRange from: 5 to: 10])
{
    NSInteger i = (NSInteger) _i_;
    printf("%ld\n", (long) i);
}

The casting here is taken from 64-Bit Transition Guide. You could just use long instead of NSInteger, but I wanted to point out that NSInteger is being used internally. The code tagged 1.0.1 reflects this.

Problem number two: casting between id and NSInteger is evil with a capital “E”. With garbage collection off, it’s playing with fire. Someone is bound to treat the integer as an object and cause havoc. With garbage collection on, the collector is bound to get confused at some point if the integer happens to point to a real object. In Bill Bumgarner’s words:

A very very bad idea indeed. Don’t do that.

So file this under “playing with fire”, though it’s still an interesting look at how fast enumeration works. Stick with regular old for loops to iterate over integers.