Parmanoir

8 ways to use Blocks in Snow Leopard

Snow Leopard introduced Blocks. They are pieces of code that can be saved and passed around, making ObjC even more dynamic. They capture the scope around them, that is the local variables of your functions, and let you use it. They take arguments you specify and let you call them anytime. Blocks are like function pointers, except they don't suck. Here's a few use cases for this new beast :

1 Custom sorting To sort an NSArray in a custom way, you need to write a new method and pass its selector to the sorting function. With blocks, just sort in-place :

id sortedArray = [myArray sortedArrayUsingComparator:
	^(id a, id b)
	{
		if (a.someValue > b.someValue && a.otherValue > b.otherValue) return NSOrderedAscending;
		... some more sorting code
	}];

2 Enumeration Looking for an object in an array can be done with a block enumerator. Set stop to YES to stop enumeration.

[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
	{
		NSLog(@"obj %@", obj);
		if (...terminatingCondition...)	*stop = YES;
	}];

This is the least interesting use of blocks. The for .. in is much more readable :

for (id obj in myArray)
{
	if (...terminatingCondition...) break;
}

The one advantage enumerateObjectsUsingBlock: has is the current index. enumerateObjectsWithOptions:usingBlock: lets you go backwards.

3 Just-this-scope functions You need to call a piece of code multiple times but don't feel like adding a new method to your class just to accomplish this one task. For instance, protocol_copyMethodDescriptionList(protocol, isRequired, isInstance, count) needs multiple calls to enumerate all protocol methods. Instead of creating a new method, use a block :

- (void)enumerateProtocolMethods:(Protocol*)p
{
	// Custom block, used only in this method
	void (^enumerate)(BOOL, BOOL) = ^(BOOL isRequired, BOOL isInstance) {
		unsigned int descriptionCount;
		struct objc_method_description* methodDescriptions = 
				protocol_copyMethodDescriptionList(p, isRequired, isInstance, &descriptionCount);
		for (int i=0; i<descriptionCount; i++)
		{
			struct objc_method_description d = methodDescriptions[i];
			NSLog(@"Protocol method %@ isRequired=%d isInstance=%d", 
				NSStringFromSelector(d.name), isRequired, isInstance);
		}
		if (methodDescriptions)	free(methodDescriptions);
	};
	// Call our block multiple times with different arguments 
	// to enumerate all class, instance, required and non-required methods
	enumerate(YES, YES);
	enumerate(YES, NO);
	enumerate(NO, YES);
	enumerate(NO, NO);
}

4 Common cleanup I trust we've all done this :

- (id)veryFragileDaisyChain
{
	... init 1
	if (condition1) {
		... init 2
		if (condition2) {
			... init 3
			if (condition3) {
				... init 4
				if (condition4) {
					// At last ! Our shiny new object !
				}
				... init 3 cleanup
			}
			... init 2 cleanup
		}
		... init 1 cleanup
	}
}

How about a block ?

- (id)veryFragileDaisyChain
{
	// Let's declare everything in advance
	__block id obj1 = nil, obj2 = nil, obj3 = nil;
	// Cleanup block
	void(^cleanup)() = ^{
		if (obj3)	... cleanup
		if (obj2)	... cleanup
		if (obj1)	... cleanup
	};

	// Onward.
	... init code 1
	if (!condition1)	return cleanup(), nil;

	... init code 2
	if (!condition2)	return cleanup(), nil;

	... init code 3
	if (!condition3)	return cleanup(), nil;

	... init code 4
	if (!condition4)	return cleanup(), nil;

	// Done.
	return shinyNewObject;
}

5 Functional style Nicolas seriot wrote about functional code in ObjC before Snow Leopard was out, hinting at using blocks to implement functional style code in ObjC. Strangely enough, Snow Leopard didn't implement these. NSArray does have a indexesOfObjectsPassingTest: method, but it returns a NSIndexSet instead of an array ?! With blocks, we can implement a filter method that will create a new array collecting objects matching our block condition.

@implementation NSArray(FunctionalStyle)
- (NSArray*)filter:(BOOL(^)(id elt))filterBlock
{
	// Create a new array
	id filteredArray = [NSMutableArray array];
	// Collect elements matching the block condition
	for (id elt in self)
		if (filterBlock(elt))	[filteredArray addObject:elt];
	return	filteredArray;
}
@end

This removes the need to loop over array element ourselves when we only want to extract elements. Just supply a condition and voilà ! A new array.

// Return a new array containing elements greater than 4
id filteredArray = [myArray filter:^(id elt)	{
			return [elt intValue] > 4;
		}];

6 List comprehensions are a shorthand way of creating arrays.

// This shorthand Javascript
var s = [2*i for (i in 100) if (i*i>3)]
// is equivalent to
var newArray = []
for (var i=0; i<100; i++)
	if (i*i>3) newArray.push(2*i)

We can implement the same with blocks, just a little more verbosely :

@implementation NSArray(ListComprehensions)
// Create a new array with a block applied to each index to create a new element
+ (NSArray*)arrayWithBlock:(id(^)(int index))block range:(NSRange)range
{
	id array = [NSMutableArray array];
	for (int i=range.location; i<range.location+range.length; i++)
		[array addObject:block(i)];
	return	array;
}
// The same with a condition
+ (NSArray*)arrayWithBlock:(id(^)(int index))block range:(NSRange)range if:(BOOL(^)(int index))blockTest
{
	id array = [NSMutableArray array];
	for (int i=range.location; i<range.location+range.length; i++)
		if (blockTest(i))		[array addObject:block(i)];
	return	array;
}
@end

Then call with a block and a range, and an optional condition :

// Just block and range
id newArray = [NSArray 
	arrayWithBlock:^(int i) {	return [NSNumber numberWithInt:i*2]; }
	range:NSMakeRange(5, 10)];

// Optional condition : keep only multiples of 3
id newArray = [NSArray 
	arrayWithBlock:^(int i) {	return [NSNumber numberWithInt:i*2]; }
	range:NSMakeRange(5, 10)
	if:^(int i) { return i%3 == 0; } ];

7 Async processing This is a simple piece of Grand Central Dispatch : enqueue an asynchronous job. The function will return immediately while the block performs in the background in another thread. Use performSelectorOnMainThread: to update your GUI afterwards.

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
		
	// Some looong computation in here ...

	// [NSThread isMainThread] tells us that calling [self taskDone] is done with the dispatch thread
	// Use this to call our end of job notification on the main thread
	[self performSelectorOnMainThread:@selector(taskDone) withObject:nil waitUntilDone:NO];
	});

8 Async monitoring Another piece of GCD, lifted from the Concurrency Programming Guide.

You can use GCD to monitor asynchronously resources. In this example, a dispatch source is set to watch when a file is renamed. One block handles notification, another handles common cleanup.

// Create a dispatch source and start watching it
- (dispatch_source_t)watchFileRename:(NSString*)fileName
{
	int fd = open([fileName UTF8String], O_EVTONLY);
	if (fd == -1)	return NULL;
	[fileName retain];
	
	// Cleanup block
	void(^cleanup)() = ^{
		close(fd);
		[fileName release];
	};

	// Create source
	dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,
		fd, DISPATCH_VNODE_RENAME, 
		dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
	if (!source)  return cleanup(), NULL;

	// Dispatch source handler
	dispatch_source_set_event_handler(source, ^{
		// *** Async event handler *** 
		// Called when the file was renamed.		
	});
	// Dispatch source destructor
	dispatch_source_set_cancel_handler(source, ^{ cleanup(); });
	// Start watching
	dispatch_resume(source);
	return	source;
}

// Release a dispatch source
- (void)releaseDispatchSource:(dispatch_source_t)source
{
	if (!source)	return;
	dispatch_source_cancel(source);
	dispatch_release(source);
}

// Start watching file rename. Call releaseDispatchSource when done.
dispatch_source_t source = [self watchFileRename:@"/FileToWatch"];
There are even more uses of blocks, as Grand Central Dispatch uses them extensively to perform concurrent tasks. I encourage you to read the Concurrency Programming Guide.

KP
2009 09 21

A good list. You missed the concurrency options on the Foundation collections enumeration methods though, which some will find very convenient.

NSDictionary's enumeration methods provide an additional convenience; it passes both the key and the value for every entry in the dictionary. No more enumerating over the keys, then getting the values separately.

NSIndexSet also has a very useful enumeration method now. No more need to use the awkward -firstIndex and -indexGreaterThanIndex: approach.

There are also two NSString methods: enumerateSubstringsInRange:options:usingBlock:, which will let you enumerate lines, paragraphs, composed character sequences, words, or sentences, and the convenience method, enumerateLinesUsingBlock:.

Also, you have an error in #4. obj1-obj3 need to be __block variables. Otherwise, the block just captures the pointer values at the point where it is created.

Patrick Geiller
2009 09 21

Thanks for the extra info and the __block bugfix !

Very nice list of uses. Thanks for posting.


Follow me on Twitter
Planet Cocoa
Cocoa.fr

2011 02 22Distance field
2010 07 202Binding through NSApp
2010 05 122Forwarding invocations
2010 02 272Core Image black fringes
2010 02 21Quickest Way to Shell
2010 02 08Who's calling ?
2009 09 2138 ways to use Blocks in Snow Leopard
2009 08 182Bracket Mess
2009 08 124Taming JavascriptCore within and without WebView
2009 04 15Debugging with Activity Monitor
2009 03 25How Core Image Color Tracking works
2009 03 1510Custom NSThemeFrame
2009 03 10Which framework is running ?
2009 03 074CoreUI can paint pretty big
2009 02 18Localization with functions
2009 01 30Did you forget to nest alloc and init?
2009 01 16JSCocoa on the iPhone
2009 01 11Mixing WebView and JavascriptCore
2009 01 09Badge overflow
2009 01 09Find your Garbage Collection leaks with Instruments

Powered by MediaWiki