Here's a use for blocks that applied itself to my project; replacing delegates and protocols (in certain situations).
The Problem
Say you need to asynchronously load data from a server. You might have a method that needs to PUT to a path (with data), and then eventually, when the task is completed, send the results to the method caller.
Delegate and Protocol Solution
Here's our method signature from our client, call it AppClient:
- (void)putToPath:(NSString *)path withData:(id)data;
We can't include the data in the return for this method since it's asynchronous (meaning it doesn't wait for the task to be completed to do other things, like run the next line of code). Instead, we construct a protocol:
@protocol AppClientRequestDelegate
- (void)appClient:(AppClient *)appClient didPutToPath:(NSString *)path withData:(id)sentData andReturnedData:(id)recievedData;
@end
Then your AppClient class would create a property like so:
@property (weak, nonatomic)id requestDelegate;
The caller of the putToPath... method would set his instance of AppClient's requestDelegate property to self, and implement the method, then verify the correct request using the path and sentData parameters, and do something-or-other with the receivedData parameter.
Our caller's code would look like this:
- (void)syncData:(id)data {
[self.appClient putPath:@"/posts/9" withData:data];
}
- (void)appClient:(AppClient *)appClient didPutToPath:(NSString *)path withData:(id)sentData andReturnedData:(id)recievedData {
if (/*path and sentData are right*/) {
// Do something with recievedData
}
}
This is all great, but it sucks when you have a bunch of PUT requests to the same path, and try to differentiate the requests from within the protocol implementation. I guess you could add another parameter to both the delegate method and the putToPath... method that specified an id for each request, but that would be messy and confusing.
Another potential concern is if you use asynchronous loading widely throughout this app; that could result in a whole lot of delegates and protocols.
Block Solution
We extend our method signature to include a block:
- (void)putToPath:(NSString *)path withData:(id)data completion:(void (^)(id returnedData))completion;
Granted, this syntax is pretty daunting, but it not only includes all the information from the protocol, but allows the caller of the method to condense all logic into one method, thereby bringing the local variables called within that method into the scope of the block's implementation.
Our caller's code would now look like this:
- (void)syncData:(id)data {
[self.appClient putToPath:@"/posts/9" withData:data completion:^(id returnedData) {
// Do something with returnedData
}];
}
Conclusion
You asked for a nice use of blocks, and I believe this is a pretty nice one; it may not be applicable to you but you can see how it not only cuts down on code mass, but makes it more readable and robust as well.