Blocks in Objective C
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.
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?

rss
Comments
No comments.