Cocoa Enumeration
Jonathan Rentzsch is back with a post about the macros he uses to tame Cocoa’s enumeration idiom. Here’s the macro I’ve been using:
#define foreach(object, enumerator) id mjtForeachEnumerator ## object = (enumerator); if ( [mjtForeachEnumerator ## object respondsToSelector:@selector(objectEnumerator)] ) mjtForeachEnumerator ## object = [mjtForeachEnumerator ## object objectEnumerator]; SEL mjtNextObjectSEL ## object = @selector(nextObject); IMP mjtNextObjectIMP ## object = [mjtForeachEnumerator ## object methodForSelector:mjtNextObjectSEL ## object]; id object; while ( (mjtForeachEnumerator ## object) && object = mjtNextObjectIMP ## object(mjtForeachEnumerator ## object, mjtNextObjectSEL ## object) )
-
Like Rentzsch’s macro, this will call
-objectEnumerator
for you. Rentzsch looks for objects that areNSEnumerator
s and doesn’t call-objectEnumerator
in that case; I look for objects that support-objectEnumerator
and do call it in that case. -
Unlike Rentzsch’s macro, the control variables are not local to the loop. His way is better, but at the time I wrote this, it either wasn’t possible to get the Objective-C compiler in C99 mode (without using Objective-C++) or I didn’t know how to do it. Thus, I glue the element name to the end of each control variable name, to try to make it unique. This lets me use
foreach
more than once in a single scope. -
After using the macro for six months or so, I added the code to cache the
IMP
for-nextObject
. All the code usingforeach
took advantage of the change with just a recompile. The speed increase was noticeable (without a stopwatch) in some cases. (In retrospect, I should probably have just used@selector(nextObject)
instead ofmjtNextObjectSEL
, since@selector
happens at link time, not runtime.) -
We use the opposite parameter order. My macro is used like:
foreach( word, words )
, which meansforeach word in words
. (In fact, if you really wanted to, you could#define
in
to be a comma and then writeforeach ( word in words )
, but I don’t recommend that.)
3 Comments RSS · Twitter
A bit late, but some people might run into the same problem as I did. I tried your code but it would not compile, complaining about a wrong lvalue assignment. Turns out to be the assignment in the while statement. && has higher precedence then =, so the = will failt.
Solution:
surroung "object = mjtNextObjectIMP ## object(mjtForeachEnumerator ## object, mjtNextObjectSEL ## object)" with another pair of ()'s
Thinking about this some more, I think there's a bug in Rentzsch's macro because it evaluates OBJ more than once.
Here is an updated version of the macro that supports loop-scope variables and static typing.