Wednesday, April 25, 2012

The Blocks

Apple introduced blocks (a segment of code that can be executed any time) in C and Objective-c. It happened in Mac OS X 10.6. Later on this feature was back-ported to Mac OS X 10.5 and iPhone by Plausible Labs.  The blocks are also called closures, because they close around variables. Also the blocks can be called lambdas.
I'd say that the blocks are the same as the regular function pointers in C. From a very general point of view, the main difference is just the symbol caret (^) before the block name instead of the asterisk (*) before the function pointer. Here is a trivial example: 

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        void (^now)(void) = ^{ 
            NSDate* moment = [NSDate date];
            NSLog(@"Now: %@", moment);
        };
        
        
        now();
    }
    return 0;
}
The program output is: [Switching to process 41322 thread 0x0] 2011-11-26 18:07:25.202 block4[41322:707] Now: 2011-11-26 16:07:25 +0000 Program ended with exit code: 0 The following program simply creates and synchronously performs a block:
#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        void (^theBlock)(int) = ^(int x) {
            printf("x = %d\n", x);
        };
        
        theBlock(12);
        
    }
    return 0;
}
typedef can be used with the blocks: 
#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        typedef NSInteger (^Multiply)(NSInteger, NSInteger);
        
        Multiply multiply = ^(NSInteger x, NSInteger y) 
        { 
            NSLog(@"Entering %s", __FUNCTION__);
            NSInteger z = x * y;
            return z; 
        };
        
        NSLog(@"%li * %li = %li", 5, 6, multiply(5, 6));
    }
    return 0;
}

The main purpose of the blocks is the dispatching them in Grand Central Dispatch. For example:

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{
    @autoreleasepool {
        printf("Hello\r");
        dispatch_queue_t queue = dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_sync(queue,  ^{ printf("This is the block\n"); } );
    }
    return 0;
}

and here is the output console:


[Switching to process 38217 thread 0x0]
Hello
This is the block
Program ended with exit code: 0
In a more formal way, that the block is a self-contained unit of a work, an arranged part of the source code, very similar to the C-style function. The block may contain own variables and can access the variables from outside its own lexical scope. The block can read values of variables defined in the parent scope. Variables, accessed by the block, are copied to the block data structure and can be used by the block later. It's preferred to keep these variables in the read-only format. However, the blocks can use variables declared with the keyword __block in the parent scope prepended to return data back from the blocks.

That's, actually, it. And this is an example of practical use of the blocks - enumeration of the array: 
#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSArray* array = [NSArray arrayWithObjects:@"First", @"Second", @"Third", nil];
        [array enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL* stop)
         {
             NSLog(@"%@", object);
         }];
    }
    return 0;
The following example is interesting because the block is used here as a parameter in the method of a class: 
#import <Foundation/Foundation.h>

@interface Test : NSObject

- (NSInteger)calculate:(NSInteger)number withBlock:(NSInteger (^)(NSInteger))block;

@end

@implementation Test

- (NSInteger)calculate:(NSInteger)number withBlock:(NSInteger (^)(NSInteger))block
{
    NSLog(@"the number is %li", number);
    NSInteger result = block(number);
    return result;
}

@end

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        Test* test = [[Test alloc] init];
        NSInteger result = [test calculate:5 withBlock:^(NSInteger number) {
            return number * number;
        }];
        NSLog(@"the result is %li", result);
        [test release];
    }
    return 0;
}
The output: 

[Switching to process 41948 thread 0x0]
2011-11-26 18:33:44.383 block5[41948:707] the number is 5
2011-11-26 18:33:44.385 block5[41948:707] the result is 25
Program ended with exit code: 0

More about the blocks: 
1. iOS Developer Library. A Short Practical Guide to Blocks. 
2. iOS Developer Library. Blocks Programming Topics.
3. Language Specifications For Blocks.

No comments:

Post a Comment