Introduction
When Mac OS X 10.6 was introduced, the Mac got a very powerful duo of developer tools that made development on a lot easier. With iOS 4.0 developers finally got access to these tools as well. These tools are known as Blocks & Grand Central Dispatch. This article is not an introduction to these technologies as i've already covered them before here
http://cocoasamurai.blogspot.com/2009/09/guide-to-blocks-grand-central-dispatch.html. What this article is, is a introduction to some design patterns I use and have seen other developers use in 1 handy article.
Many of the design patterns I am displaying here are actually in use in my
Zangetsu Framework which is open source under the MIT License on Github.
Iterative Callbacks
/**
Ruby inspired iterator for NSArray in Objective-C
*/
-(NSArray *)cw_each:(void (^)(id obj))block
{
for(id object in self){
block(object);
}
return self;
}
Blocks are really good for doing callbacks and one of the things you can do already is enumeration. Now while Objective-C is definitely expressive with high level english like syntax, sometimes it is unnecessarily verbose in my opinion. When I started reading through my Ruby 1.9 book one thing I loved was ruby's enumeration with each. It's so simple and while it could be slightly more verbose it's succinct enough. So I decided to implement each in Objective-C with blocks since I could actually make it work. The result is what you see above.
Additionally you can use grand central displatch and make this asynchronous.
-(NSArray *)cw_eachConcurrentlyWithBlock:(void (^)(id obj))block
{
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for(id object in self){
dispatch_group_async(group, queue, ^{
block(object);
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
return self;
}
In this example I am using dispatch groups and getting a reference to a global queue and then basically enumerating over the objects and then calling dispatch_group_wait() to make sure that all the blocks complete. This type of thing is great when you want to be both asynchronous, but also ensure that the method itself is synchronous.
/**
Simple mapping method using a block
*/
-(NSArray *)cw_mapArray:(id (^)(id obj))block
{
NSMutableArray *cwArray = [[NSMutableArray alloc] initWithCapacity:[self count]];
for(id obj in self){
[cwArray addObject:block(obj)];
}
return cwArray;
}
Here is a good example of putting that callback capability to good use. In this example I wanted to map one array to another one. This is basically exactly the same as my cw_each method above except that inside this method I am creating an array that the new (mapped) array is being stored in and returning an object from the block. If you wanted to you could be selective in mapping an array to another by simply checking that the object returned from the block is not nil and only then adding it to an array you are mapping to.
Enclosing Blocks
Another clever use for blocks is to enclose them in between other code for various purposes. For example in this case I was experimenting with only executing a block when I am building debug code. So instead of doing...
#ifdef DEBUG
NSString *myString = ...
//do something with string
#endif
I think this is more clear and less cluttered
//in .h
typedef void (^DebugBlock)(void);
void CWInDebugOnly(DebugBlock block);
//in .m
void CWInDebugOnly(DebugBlock block)
{
#ifdef DEBUG
block();
#endif
}
This way in code I can do
CWInDebugOnly(^{
NSString *myString = ...
//do something with string....
});
This relies on DEBUG being defined only in your debug builds. I have this set up to only be defined in my Debug and Analyze configurations. I've also seen
Mike Ash cleaverly do this exact same thing with NSAutoreleasePools like so...
//in .h
typedef void (^VoidBlock)(void);
void inAutoreleasePool(VoidBlock block);
//in .m
void inAutoreleasePool(VoidBlock block)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
block();
[pool drain];
}
Completion Blocks
Completion Blocks are especially good things to have around. They are very useful on objects that are performing some operation, usually asynchronously, and we would like to know when the operation has been completed. We may or may not care about the implementation details, but we need a mechanism to do something when this is done. This is where completion blocks are excellent helpers. In the past before blocks we may have used notifications or delegates and created methods especially to deal with this 1 specific application. Now it's as easy as assigning a block as a variable and executing it when you are done. One prime example of this is NSOperation. You can create it, add it to a queue, and go on and do other things, and once the operation is done it executes your completion block. These completion blocks are also excellent because we can do these things on threads in grand central dispatch and simply in the completion block dispatch back to the main thread and update the user interface
NSData *data = //..data from a file or internet
MyOperation * operation = [[MyOperation alloc] initWithDataToParse:data];
[operation setCompletionBlock:^(NSString *result){
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self updateUIWithString:result];
}];
}];
[queue addOperation:operation];
This code is easy to read and follow what it is doing. Let's see how we can do this in our classes.
//.h
typedef void (^TaskCompletionBlock)(void);
@interface MyClass : NSObject {}
@property(nonatomic,copy) TaskCompletionBlock block;
-(void)doTask;
@end
//.m
@implementation MyClass
@synthesize block;
-(id) init
{
self = [super init];
if(self){
//
}
return self;
}
-(void)doTask
{
//do some task here...
if(self.block) {
self.block();
}
}
@end
Dispatch_Once
Out of all the API calls I saw in grand central dispatch the one that initially impressed me least was
dispatch_once
. However after reading its documentation and using it a bunch I realized how useful this one API call is. Not only it is a dead simple way to ensure that code only gets executed once, but it is also thread safe.
From the API...
"If called simultaneously from multiple threads, this function waits synchronously until the block has completed."
This means its perfectly safe to run code like this from multiple threads...
+(NSString *)coreDataKeyNameForWebAPIKey:(NSString *)name
{
static dispatch_once_t dictPrec;
dispatch_once(&dictPrec, ^{
coreDataKeyMappings = [[NSDictionary alloc] initWithDictionary:NSDICT(
kCDEntityNameKey, kWebEntityNameKey,
kCDEntityDateKey, kWebEntityDateKey,
kCDEntityTimeStampKey, kWebTimeStampKey,
KCDEntityUpdateKey, kWebEntityUpdateKey)];
});
NSString *translatedKey = [coreDataKeyMappings valueForKey:name];
return translatedKey;
}
Conlcusion
Grand Central Dispatch and Blocks are just awesome. Even just blocks by itself makes many things possible that weren't before. With grand central dispatch and blocks many problems that have been traditionally associated with multithreading go away. These 2 technologies don't eliminate all these problems, but they certainly do make them much more manageable.