An Objective-C block is technically termed a “closure”. From Wikipedia:
A closure is a first-class function with free variables that are bound in the lexical environment
Ruby methods can accept a single block as an argument. The following would be used in place of a for loop in Ruby:
# Prints a list of integers from 0 4
5.times { |i| puts i }
Or to, say, filter a range of numbers:
# Returns [2, 4]
evenNumbers = (1..5).select { |i| i.even? }
JavaScript uses anonymous functions which (in JavaScript) close over the local variables to handle callbacks. For instance:
var nameField = isFirstName ? $('#firstName') : $('#lastName')
$.get({'http://exapmle.com/foo.json', function(data) {
nameField.val(data['name'])
$('#email').val(data['email'])
}})
Other than replacing the * with a ^, the syntax is identical to a function pointer:
# Creates a function pointer called "name_of_function_pointer"
return_type (*name_of_function_pointer)(arg1_type, arg2_type);
# Creates a block pointer called "name_of_block_pointer"
return_type (^name_of_block_pointer)(arg1_type, arg2_type);
The difference is that while functions are be created at compile time and can “close over” global variables, blocks are created at runtime and close over local variables.
In Objective-C, a block looks like this:
^ BOOL (int num) {
return num & 1;
}
The return type can be inferred, so this can be simplified to:
^(int num) {
return num & 1;
}
For a block with no arguments, e.g.:
^(){
doSomething();
}
…the parentheses can be (and usually are) omitted:
^{
doSomething();
}
To clarify your code, you can use typedefs:
typedef BOOL (^testBlock)(int);
To use this block, you might do something like this:
testBlock isEven = ^(int num) { return num & 1; };
int *evenNumbers = filter(numbersFromOneToFive, isEven);
Or more concisely:
int *evenNumbers = filter(numbersFromOneToFive, ^(int num){
return num & 1;
});
In both cases filter would take an array of ints and return a new array of only those elements for which the block returns YES.
In the preceding case, you could just as easily have written an isEven function and passed that to an appropriate filter function. A better example of the usefulness of blocks might be the following:
int* numbersDivisibleBy(int *numbers, int divisor) {
return filter(numbers, ^(int num) {
return (num % divisor) == 0;
});
}
Now the runtime makes a const copy of divisor and associates it with the block.
In Objective-C, many APIs now use blocks to handle common tasks:
[mySet objectsPassingTest:^(id obj, BOOL *stop) {
return [obj testValue:value];
}];
[aDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
NSLog(@"%@ is %@", key, value);
}];
The preceding were a examples of synchronous blocks. Here is some actual code for an asynchronous use of blocks:
- (void)loadDataWithURL:(NSURL *)url complete:(void (^)(NSData *))block {
__block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request setCompletionBlock:^{
block([request responseData]);
}];
[request startAsynchronous];
}
This will run the block that is passed in when the request has finished, with the downloaded data as an argument.
(The __block storage type modifier indicates that the value of the variable may be mutated inside the block. This also prevents the runtime from retaining a reference to the block.)
You might call this function like so:
[webService loadDataWithURL:url complete:^(NSData *data){
self.imageView.image = [UIImage imageWithData:data];
}];
One of the big rules of Cocoa development is “don’t block the main thread”. The main thread handles all UI interaction, and that is pretty much all it should be doing.
But don’t do this with more than one or two threads at a time:
- (void)loadDataWithURL:(NSURL *)url complete:(void (^)(NSData *))block {
dispatch_async(some_queue, ^{
NSData *data = [NSData dataWithContentsOfURL:url];
dispatch_async(main_queue, ^{
block(data);
});
});
}
Try to use event-driven APIs (from Apple, or something like ASIHTTPRequest) instead of creating a bunch of threads and having them make blocking calls.
There is a subtle bug with UIWebViews and blocks. UIWebView instances will intentionally crash your app when released from a background thread. Here is a workaround to place in any view controller that both uses blocks and has an outlet to a web view:
- (oneway void)release
{
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:YES];
} else {
[super release];
}
}
Blocks are only supported in:
However, there is a package called PLausible Blocks that contains a hacked GCC that will let blocks run on:
However some APIs (e.g. NSBlockOperation) are not available.
Further reading: