John Gruber:
Doing your guerrilla UI hacking by way of scripting and plug-ins solves all the forking issues mentioned earlier—you’re not modifying the application itself, on disk, so you won’t interfere with system updates or the interaction between the application and the rest of the system. And it’s easier to share your work with others—“try my script” is a lot more appealing than “try my custom binary of iCal.”
I’ve updated the macro that I use to iterate over Cocoa collections. As before, the idea is that you write foreach( object, collection )
, where object
is the name of the loop variable and collection
is either an NSEnumerator
or an object that responds to -objectEnumerator
. collection
is only evaluated once, and IMP
-caching is used to speed up the calls to -nextObject
.
Unlike the old version, all the variables are local to the loop; it does not pollute the outer scope. This requires that you set the “C Language Dialect” in Xcode to C99. Also, there is a new macro called foreacht
that lets you specify a type for the loop variable. Then you can write foreach( NSString *, string, collection )
, and if you write something like [string count]
you’ll get a compiler warning. Here’s the code:
#define foreachGetEnumerator(c) \
([c respondsToSelector:@selector(objectEnumerator)] ? \
[c objectEnumerator] : \
c)
#define foreachGetIMP(e) \
[foreachEnum methodForSelector:@selector(nextObject)]
#define foreachCallIMP(imp, e) \
((IMP)imp)(e, @selector(nextObject))
#define foreachGetFirst(imp, e) \
(e ? foreachCallIMP(imp, e) : nil)
#define foreach(object, collection) \
for ( id foreachCollection = collection, \
foreachEnum = foreachGetEnumerator(foreachCollection), \
foreachIMP = (id)foreachGetIMP(foreachEnum), \
object = foreachGetFirst(foreachIMP, foreachEnum); \
object; \
object = foreachCallIMP(foreachIMP, foreachEnum) )
#define foreacht(type, object, collection) \
for ( type foreachCollection = (id)collection, \
*foreachEnum = (id)foreachGetEnumerator((id)foreachCollection), \
*foreachIMP = (id)foreachGetIMP((id)foreachEnum), \
*object = foreachGetFirst(foreachIMP, foreachEnum); \
object; \
object = foreachCallIMP(foreachIMP, foreachEnum) )
Update: Matt Neuberg (who prompted this post by sending me his modification of my original foreach
to add the type
parameter) didn’t like the look of the casts and suggested using nested one-time for
loops instead:
#define foreachGetEnumerator(c) \
([c respondsToSelector:@selector(objectEnumerator)] ? \
[c objectEnumerator] : \
c)
#define foreacht(type, object, collection) \
for ( id foreachCollection = collection; \
foreachCollection; \
foreachCollection = nil ) \
for ( id foreachEnum = foreachGetEnumerator(foreachCollection); \
foreachEnum; \
foreachEnum = nil ) \
for ( IMP foreachNext = [foreachEnum methodForSelector:@selector(nextObject)]; \
foreachNext; \
foreachNext = NULL ) \
for ( type object = foreachNext(foreachEnum, @selector(nextObject)); \
object; \
object = foreachNext(foreachEnum, @selector(nextObject)) )
And with that foreacht
, you can now define foreach
like so:
#define foreach(object, collection) foreacht(id, object, (collection))
Cocoa Macros Objective-C Programming