Index Set Foreach

Download

MFIndexSetForeach 1.0 (2 Kb)
First release. December 12 2011.

Introduction

Index sets can be a little annoying in Objective-C. Unlike arrays, sets, and dictionaries, you can’t iterate over them using a straightforward for (a in b) loop. That’s because unlike other Objective-C containers, index sets contains integers, not objects.

Since Mac OS X 10.6 you can use a block to iterate over an index set, but this isn’t always ideal: breaking out of the loop is more complicated, and returning from the block doesn’t return from the outer function.

In an ideal world you could write this and it’d work, but alas it doesn’t:

for (NSUInteger index in indexSet)
{
    // loop body
}

What MFIndexSetForeach achieves using some macro wizardry is this:

MFIndexSetForeach(index, indexSet)
{
    // loop body
}

MFIndexSetForeach iterates over all indexes in a set. It uses the getIndexes:maxCount:inIndexRange: method of NSIndexSet to fill a small buffer of indexes, iterates over the buffer then refills the buffer and continue iterating until all indexes have been iterated over. This is quite similar to how for (a in b) works.

This loop behaves in every way like you’d expect a loop to behave: you can break from it, you can continue in it, and if you return it’ll truly return from the function:

MFIndexSetForeach(index, indexSet)
{
    if (index % 33) continue;
    if (index % 22) break;
    if (index % 11) return YES;
}

Oh, and the braces are optional too.

Have fun with it.

How to Use

MFIndexSetForeach is a macro in a header file of the same name. Just add the “MFIndexSetForeach.h” file to your project, then in the file where you need a loop write:

#import "MFIndexSetForeach.h"

You’re all set.

How does it work?

The equivalent generated code would look somewhat like this:

NSRange _indexRange = NSMakeRange(0, NSUIntegerMax);
for (NSUInteger index,
     _bufferIndex = _bufferLength-1,
     _indexCount = 0,
     _indexBuffer[_bufferLength];
     _MFIndexSetForeachNextIndex(
         &_bufferIndex, &_indexCount, indexSet, _indexBuffer,
         _bufferLength, &_indexRange, &index);
    )
{
    if (index % 33) continue;
    if (index % 22) break;
    if (index % 11) return YES;
}

where, _MFIndexSetForeachNextIndex is a short inline function doing all the work. The true generated code will look a little more cryptic since variable names are generated in a way that avoids clashes.

License

MFIndexSetForeach is available under the terms of the Boost Software License 1.0.

History

Index Set Foreach 1.0


  • © 2003–2017 Michel Fortin.