I've long had a fascination with SmallTalk style blocks in Objective-C. So much so, that I learned a lot about how C and GCC work when I implemented them on the primitive of GCCs nested functions myself.
[browser:open-source/closures-in-objc/trunk Heres the Source]
Of course, just as I had it working, Apple deprecated GCCs nested functions, as they where implemented using a trampoline on the stack. And of course, a trampoline being executable code they where out when the non executable stack came in.
Ah well.
BUT, Apple just released with Snow-Leopard a new compiler feature [Blocks]!
Yay, closures in C!
So here's how it looks if you implement the Smalltalk collection iteration protocoll in ObjC. (Note: this of course are not propper ObjC-Names, but each Smalltalker will none the less get a tear in their eye when they see this)
#import <Foundation/Foundation.h> @implementation NSArray (BlocksTest) - (void) do:(void (^)(id))aBlock; { // Take care, -enumerateObjectsUsingBlock: wraps an auto-release pool around the iteration [self enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) { aBlock(obj); }]; } - (NSArray *) collect:(id (^)(id))aBlock; { id collectedItems = [NSMutableArray arrayWithCapacity:[self count]]; [self do:^(id each) { [collectedItems addObject:aBlock(each)]; }]; return [collectedItems copy]; // REFACT: consider to drop copy } - (id) detect:(BOOL (^)(id))aBlock; { // Take care, -enumerateObjectsUsingBlock: wraps an auto-release pool around the iteration __block id resultObject = nil; [self enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) { if (aBlock(obj)) { resultObject = obj; *stop = YES; } }]; return resultObject; } - (id) detect:(BOOL (^)(id))aBlock ifNone:(id (^)())errorBlock; { id foundElement = [self detect:aBlock]; if (foundElement) return foundElement; else return errorBlock(); } - (id) inject:(id)aValue into:(id (^)(id, id))aBlock; { // Need to take care with retain here, because apple wraps an auto-release pool around the block iterator. :/ __block id collected = [aValue retain]; [self do:^(id each){ collected = [aBlock([collected autorelease], each) retain]; }]; return [collected autorelease]; } - (NSArray *) reject:(BOOL (^)(id))aBlock; { id selectedObjects = [NSMutableArray arrayWithCapacity:[self count]]; [self do:^(id each){ if (aBlock(each)) return; [selectedObjects addObject:each]; }]; return [selectedObjects copy]; // REFACT: consider to drop copy } - (NSArray *) select:(BOOL (^)(id))aBlock; { return [self reject:^(id each){ return (BOOL) ! aBlock(each); }]; } @end #define log(objcObject) fprintf(stdout, "%s\n", [[objcObject description] UTF8String]) int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; id array = [NSArray arrayWithObjects:@"first", @"second", @"third", nil]; log(@"\ndo:"); [array do:^(id each){ log(each); }]; log(@"\ncollect:"); log([array collect:^id(id each){ return [each uppercaseString]; }]); log(@"\ndetect:"); log([array detect:^(id each){ return [each isEqual:@"second"]; }]); log(@"\ndetect:ifNone:"); log([array detect:^(id each){ return NO; } ifNone:(id)^{ return @"Yeehaw!"; }]); log(@"\ninject:into:"); log([array inject:@"" into: ^ id (id concatenation, id element){ return [concatenation stringByAppendingString:element]; }]); log(@"\nreject:"); log([array reject:^(id each){ return [each hasSuffix:@"nd"]; }]); log(@"\nselect:"); log([array select:^(id each){ return [each hasSuffix:@"d"]; }]); [pool drain]; return 0; }
Ain't that pretty?
[browser:open-source/smalltalk-like-iterators/trunk Here's the current version!]