Archive for July 15, 2006

Saturday, July 15, 2006 [Tweets] [Favorites]

Not Open Sourcing Apple’s Apps

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.”

Cocoa foreach Macro

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))