2

I've just read about blocks, and I understood that they just encapsulate information as a normal method, but with their own strong referenced data. I'm wondering what a good use of blocks could be?

chuff
  • 5,846
  • 1
  • 21
  • 26
Baxter Lopez
  • 1,110
  • 1
  • 9
  • 14
  • 1
    Continue to read, I am sure you will find the examples you ask for. – Matthias Jun 29 '13 at 04:12
  • They certainly are great for creating confusion. – Hot Licks Jun 29 '13 at 04:13
  • possible duplicate of [Why do we need blocks, function literals, closures in programming languages?](http://stackoverflow.com/questions/17058391/why-do-we-need-blocks-function-literals-closures-in-programming-languages) – rob mayoff Jun 29 '13 at 04:13
  • If anything, they encapsulate *behaviour* and are more similar to *functions* than methods. They don't have intrinsic access to an object's variable (unless passed as arguments) –  Jun 29 '13 at 04:15
  • this question is "on hold". the short answer is: they are like function pointers, but they offer far fewer restrictions/much greater flexibility than function pointers. they also can perform reference counting or copy their state for relocation to other contexts. so the easy way to view it (if you know C) is to imagine how your programs would look (and how they would fail, e.g. using ref counting) if function pointers were used instead of blocks. Result: you would end up writing a lot of utility classes and losing type safety. – justin Jun 29 '13 at 06:44

3 Answers3

11

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<AppClientRequestDelegate> 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.

aopsfan
  • 2,451
  • 19
  • 30
1

Blocks help you write better code in a number of ways. Here are two.

More Reliable Code

One advantage is more reliable code. Here's a specific example.

Prior to iOS 4.0, to animate views, you had to use the beginAnimations:context: and commitAnimations messages, like this:

[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelay:1.0];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];

self.basketTop.frame = basketTopFrame;
self.basketBottom.frame = basketBottomFrame;

[UIView commitAnimations];

Note that you have to remember to put in the call to commitAnimations, or your app will misbehave. The compiler will not warn you that you forgot to call commitAnimations.

In iOS 4.0, Apple added blocks, and they added new ways to animate your views using blocks. For example:

[UIView animateWithDuration:0.5 delay:1 options:UIViewAnimationOptionCurveEaseOut animations:^{
    self.basketTop.frame = basketTopFrame;
    self.basketBottom.frame = basketBottomFrame;
} completion:nil];

The advantage here is that there's no possibility of forgetting to commit the animation. The compiler will give you a syntax error if you forget to put } at the end of the block or ] at the end of the method. And Xcode will autocomplete the message name, so you don't have to remember how it's spelled.

Better Code Organization

Another advantage is better code organization. Here's an example.

Suppose you want to send a UIImage to a server. Converting the image to PNG data might take some time, so you don't want to block the main thread while doing it. You want to do it in the background, on another thread. Prior to iOS 4.0, you might decide to use an NSOperationQueue. First, you need to make a subclass of NSOperation to do the work1:

@interface SendImageToServerOperation : NSOperation

@property (nonatomic, retain) UIImage *image;
@property (nonatomic, retain) NSURL *serverURL;

@end

@implementation SendImageToServerOperation

- (void)main {
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.serverURL];
    request.HTTPBody =UIImagePNGRepresentation(self.image);
    NSURLResponse *response;
    NSError *error;
    [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
    // Handle response/error here?
}

@end

Then, to actually do it, you need to create an operation and put it on a queue:

- (void)sendImage:(UIImage *)image toServerURL:(NSURL *)serverURL {
    SendImageToServerOperation *operation = [SendImageToServerOperation new];
    operation.image = image;
    operation.serverURL = serverURL;
    [backgroundQueue addOperation:operation];
}

The code is spread out. Starting in iOS 4.0, you can use blocks (and the new GCD framework2) to put it all together:

- (void)sendImage:(UIImage *)image toServerURL:(NSURL *)serverURL {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:serverURL];
        request.HTTPBody =UIImagePNGRepresentation(image);
        NSURLResponse *response;
        NSError *error;
        [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
        // Handle response/error here?
    });
}

You don't have to make a new class or even a separate function. You don't even have to create any extra objects. You can put the code where it's easiest to understand and maintain.


Footnote 1. This is not necessarily the best way to upload the data to the server. I chose a simple way for educational purposes. However, it is realistic to want to create the PNG data on a background thread.

Footnote 2. The NSBlockOperation class (starting in iOS 4.0) lets you use blocks directly with NSOperationQueue, if you prefer that over GCD.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
0

switching to blocks made my programs a lot more modular and flexible. For example in most cases I stopped relying on delegation, I instead pass a block (which encapsulates the variables in the parent object) and it gets the work done in a one way fashion.

In general I think that using blocks helps decouple your code (and I find using them applicable to many design patterns). Here is an example:

/*
 * here block is basically is clean up code that is supposed to execute 
 * after this method finishes its work
 */
-(void)doSomeProcess:(void(^)(void))block {
   // do stuff
   // ..
   // and when you're done
   block();
}

// caller 1
[self doSomeProcess:^{ 
   // block body:
   // dismiss UI
}];

// caller 2
[self doSomeProcess:^{
  // another block body:
  // do business logic clean up
  // dismiss UI
}];

and so many objects or callers can be calling the doSomeProcess method, but each having their own clean up work.


another example: here is another example (I literally just did it so I figured I could share it with you).. look at this unit test with KIF:

[sr performFilterAttachmentWithBlock:^(NSArray *fileBucketResults){
    for (NSMutableDictionary* fileBucketResult in fileBucketResults) {
        [elementsToAdd addObject:fileBucketResult];
        [rowsToAdd addObject:[NSIndexPath indexPathForRow:cellIndex inSection:0]];
        cellIndex++;
    }

    // note this notification
    [[NSNotificationCenter defaultCenter]
     postNotificationName:(NSString *)kFileBucketSubviewFetchedResultsFromDB
                   object:fileBucketResults];

} withQuery:query sortBy:(NSString *)kFileBucketSortBySenderName];

in KIF unit testing, there are some unit tests that rely on sending notifications.. prior to using blocks (and while using delegates).. i had to mix in testing code inside my actual code (ie this notification was actually placed in my main code).. but now thanks to blocks.. i can just put all my testing code inside a block, which in turn is placed in my unit test file (ie doesn't mix with main code).. = cleaner code! :)


another example: it makes for a nice way to hide some very specific utility/helper functions, which reduces namespace cluttering and make for cleaner code overall.. ex:

// without blocks
-(void)someMethod {

  // call this private method that does some helper stuff
  [self helperMethod];

  // we call the helper method several times in this f'n
  [self helperMethod];
}

-(void)helperMethod {
  // this method is only useful for 'some method'
  // although it's only visible within this class.. it's still
  // an extra method.. also nothing makes it obvious that 
  // this method is only applicable to 'someMethod'
  ..
}

// With blocks
-(void)someMethod {
  void(^helperMethod)(void) = ^{
    // helper block body
    // this block is only visible to 'some method'
    // so it's obvious it's only applicable to it
  }

  // call helper method..
  helperMethod();

  // .. as many times as you like
  helperMethod();

}

here is a question/answer that illustrates converting a delegation method to a block..

Community
  • 1
  • 1
abbood
  • 23,101
  • 16
  • 132
  • 246