2013-08-28

Yet another little objective-C hack, for when you just want to count:

for (NSNumber * num in [@2 to:@4]) {
    // do something with num
}

Here, [@2 to:@4] is actually the same thing as @[@2, @3, @4].

Probably of little use, but very easy to write: all it takes is a method on NSNumber and a very small NSArray subclass. Here’s the one public method:

@interface NSNumber (ObjcRange)
// returns an array of the n + 1 NSIntegers between the receiver and `end_`, inclusive.
- (NSArray*) to:(NSNumber*)end_;
@end

And all it does is creating an small subclass of NSArray.

@implementation NSNumber (ObjcRange)
- (NSArray*) to:(NSNumber*)end_
{
    return [[ObjcRange alloc] initWithNumbersFrom:[self integerValue] to:[end_ integerValue]];
}
@end

The trick is that NSArray doesn’t care where its objects are stored. They can actually be organized in a C array, a linked list, a tree, etc. The actual implementation even swaps its internal storage sometimes.

In fact, all that’s required to subclass NSArray is to override -count and -objectAtIndex:, both of which can trivially be implemented just by knowing the start and end of our range:

@implementation ObjcRange
{
    NSInteger _start,  _end;
}

- (id)initWithNumbersFrom:(NSInteger)start_ to:(NSInteger)end_
{
    self = [super init];
    if(start_ < end_) {
        _start = start_;
        _end = end_;
    } else {
        _start = end_;
        _end = start_;
    }
    return self;
}

- (NSUInteger)count
{
    return _end - _start + 1;
}

- (id)objectAtIndex:(NSUInteger)index
{
    return @(_start + (NSInteger)index);
}

@end

Here’s a github repo with the source. There’s probably much more to be done with it, feel free to hack away and let me know.