Blocks in Objective-C

Blocks in Objective – C

What is Block?

  • Blocks are language level feature which allows to create distinct segments of code that can be passed around to methods and functions as if they were values.
  • Blocks are Objective-C objects
  • As blocks are Objective C objects they can be added to Arrays and Dictionaries
  • Blocks have capability to capture values from enclosing scopes making them similar to closures or lambdas
  • Blocks can be passed as arguments to methods or functions
  • We might use Grand Central Dispatch to invoke a block in the background
  • Blocks can take other blocks as input parameter and return a block

When to use Blocks?

  • Blocks should be used where Callbacks are required, that is defining the code to be executed once a task is completed, Blocks are useful as callback because the block carries both the code to be executed on callback and the data needed during that execution
  • Blocks can be used anywhere we have a formal or informal delegate
  • Iterating through a collection can be done through a block instead of a for loop. This is applicable when the order does not matter and the enumeration can be done conncurrently
  • Completion handlers or failure handlers.

Blocks best practices

  • Its a best practice to use only one block argument to a method.
  • If the method also needs other non-block arguments, the block should come last:
Objective C blocks

Objective C blocks

Sample Code with Explanation

#import "KSAppDelegate.h"
@implementation KSAppDelegate
typedef void (^EndBlock)();
- (void)dealloc
{
    [super dealloc];
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    [self testDifferentBlockImplementations];
    [self getDataFromDatabase:self];
}

-(void)testDifferentBlockImplementations
{

//A simple Block with one input parameter and return type as int
    int multiplier = 7;
    int (^testBlock)(int) = ^(int num)
    //This is a literal block definition assigned to variable testBlock
    {
        return num *multiplier;
    };
    NSLog(@"number %d",testBlock(10)); //This will print 70

//A simple Block with two input parameters and return type as int
//    int (^addBlock)(int value1, int value2) = ^(int va1, int val2){ //This way of defining a block is also correct
    int (^addBlock)(int, int) = ^(int va1, int val2){
        return va1 + val2;
    };
    NSLog(@"addBlock %d",addBlock(10,12)); //Prints 22

//Blocks can use variables from the same scope in which it was defined.
//If blocks are defined as variables as in the sample given above, it can be used as a function.

//Two input parameters and block definition
    float (^areaBlock)(float length, float breadth); //Declare the block variable
    //Create and assign the block
    areaBlock = ^float(float len, float brd) //Defines the return type to be float
    //areaBlock = ^(float len, float brd)    //Does not define the return type. Defining the return type is not mandatory
    {
        return len * brd;
    };
    float newArea = areaBlock(10.87,87.65);
    NSLog(@"newArea is %2.2f",newArea);    //Prints 952.76

//Block with No input Parameters
    u_int32_t (^randomNumber)(void) = ^{
        return arc4random();
    };
    NSLog(@"random number is %d",(int)randomNumber);

//Closures
//Blocks can access to non local variables defined in the blocks enclosing lexical scope but outside the block itself

    NSString *firstName = @"John";
    NSString * (^getFullName)(NSString *lastName) = ^(NSString *lName){
        //        firstName = @"Jane"; //Variable cannot be assigned here untill and unless the variable is declared as a block type
        return [firstName stringByAppendingFormat:@" %@",lName];
    };
    NSLog(@"Full Name is %@",getFullName(@"Doe"));    //Full Name is John Doe

//Non Local Variables are copied and stored with the blocks are constants (read only)
//The non local variable cannot be modified

    firstName = @"Jane";
    NSLog(@"Full Name after first name is changed is still %@",getFullName(@"Doe"));
    //Full Name after first name is changed is still John Doe

//Changing a non local variable within a block
    __block NSString *title = @"Mr";
    NSString *(^getCompleteName)(NSString *name) = ^(NSString *name){
        title = @"Ms";
        return [title stringByAppendingFormat:@" %@",name];
    };
    NSLog(@"Complete Name is %@",getCompleteName(@"Jane Doe"));    //Complete Name is Ms Jane Doe

    __block int i = 0; //The block modifier serves as a memory between multiple calls to a block
    int (^count)(void) = ^ {
        i += 1;
        return i;
    };
    NSLog(@"%d", count());    // 1
    NSLog(@"%d", count());    // 2
    NSLog(@"%d", count());    // 3

//BLOCKS AS METHOD PARAMETERS
    NSArray *stringsArray = @[ @"string 1",
                               @"String 21",
                               @"string 12",
                               @"String 11",
                               @"String 02" ];

    static NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch | NSNumericSearch |
    NSWidthInsensitiveSearch | NSForcedOrderingSearch;
    NSLocale *currentLocale = [NSLocale currentLocale];

    NSComparator finderSortBlock = ^(id string1, id string2) {
        NSRange string1Range = NSMakeRange(0, [string1 length]);
        return [string1 compare:string2 options:comparisonOptions range:string1Range locale:currentLocale];
    };
    NSArray *finderSortArray = [stringsArray sortedArrayUsingComparator:finderSortBlock];
    NSLog(@"finderSortArray: %@", finderSortArray);
    /*
     Output:
     finderSortArray: (
     "string 1",
     "String 02",
     "String 11",
     "string 12",
     "String 21"
     )
     */

    NSArray *numberArray = @[@12,@13,@10,@9,@100,@109,@432];
    [numberArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        NSLog(@"index = %d  value = %d %@",idx,[obj intValue],stop?@"Yes":@"No");
    }];
    //index = 0  value = 12 Yes
    //index = 1  value = 13 Yes
    //index = 2  value = 10 Yes
    //index = 3  value = 9 Yes
    //index = 4  value = 100 Yes
    //index = 5  value = 109 Yes
    //index = 6  value = 432 Yes

//A simple block that simply executes without taking any input parameter or returining any value
    void (^callSimpleBlock)(void) = ^{
        NSLog(@"This is a block, Takes nothing, gives nothing");
    };
    callSimpleBlock();

    NSArray *array = @[@"A", @"B", @"C", @"A", @"B", @"Z", @"G", @"are", @"Q"];
    NSSet *filterSet = [NSSet setWithObjects: @"A", @"Z", @"Q", nil];
    BOOL (^test)(id obj, NSUInteger idx, BOOL *stop);
    test = ^(id obj, NSUInteger idx, BOOL *stop) {
        if (idx < 5) {
            if ([filterSet containsObject: obj]) {
                return YES;
            }
        }
        return NO;
    };
    NSIndexSet *indexes = [array indexesOfObjectsPassingTest:test];
    NSLog(@"indexes: %@", indexes);
    //indexes: [number of indexes: 2 (in 2 ranges), indexes: (0 3)]

/*
    block Completion Handler Example
*/

    [self doSomething:^{
        NSLog(@" This will get called on completion of the doSomething Method.");
    }];

/*
    Another Completion Handler Example
*/
    [self countToHundredAndReturnCompletionBLock:^(BOOL completed){
        if(completed)
        {
            NSLog(@"Hundred Counts Finished");
        }
        else
        {
            NSLog(@"Actually Completed but showing not completed. Not some Goofy code, Returning NO here");
        }
    }];

    __block BOOL found = NO;
    NSSet *dataSet = [NSSet setWithObjects: @"A", @"B", @"C",@"D",@"E",@"F",@"G", @"X", nil];
    NSString *string = @"g";

    [dataSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
        if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {
            *stop = YES;
            found = YES;
        }
    }];

//Calling a Global Block
    aGlobalBlock();

/* BEGIN BLOCK PERFORMANCE TEST 1*/
    int k=0; int l=0;
    NSDate *start1 = [NSDate date];
    for (int i=0; i<10000000;i++)
    {
        k =[self addTwoInts:10 andB:i];
    }
    NSDate *end1 = [NSDate date];

    for (int i=0; i<10000000;i++)
    {
        l = additionBlock(10,i);
    }
    NSDate *end2 = [NSDate date];
    NSTimeInterval executionTime1 = [end1 timeIntervalSinceDate:start1];
    NSTimeInterval executionTime2 = [end2 timeIntervalSinceDate:end1];
    NSLog(@"executionTime using normal methods = %f", executionTime1);
    NSLog(@"executionTime using blocks = %f", executionTime2);
    //Blocks are faster than conventional methods.
    //executionTime using normal methods = 0.057190
    //executionTime using blocks = 0.048764

/* END BLOCK PERFORMANCE TEST 1*/

/* BEGIN PERFORMANCE TEST 2*/
    NSMutableArray *dataArray =[[[NSMutableArray alloc] init] autorelease];
    NSMutableArray *mArray1 = [[[NSMutableArray alloc] init] autorelease];
    NSMutableArray *mArray2 = [[[NSMutableArray alloc] init] autorelease];
    NSMutableArray *mArray3 = [[[NSMutableArray alloc] init] autorelease];

    NSDate *dt1 = [NSDate date];
    for (int j=0; j<10000000;j++)
    {
        [dataArray addObject:[NSNumber numberWithInt:j]];
    }

    NSDate *dt2 = [NSDate date];
    int cnt = [dataArray count];
    //Using normal for loop
    for (int k=0; k<cnt;k++)
    {
        [mArray1 addObject:[dataArray objectAtIndex:k]];
    }

    //Using Fast Enumeration
    NSDate *dt3 = [NSDate date];
    for (NSNumber *num in dataArray)
    {
        [mArray2 addObject:num];
    }

    //Enumerating using Blocks
    NSDate *dt4 = [NSDate date];
    [dataArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        [mArray3 addObject:obj];
    }];
    //One of the advantage of using enumerateObjectsUsingBlock is that the current object index (idx) is passed along with the object. This option is not available in fast enumeration untill and unless we are maintaining our own index
    // The BOOL pointer allows for early return, equivalent to a break statement in a regular loop
    // Unless we actually need the numerical index while iterating, it's almost always faster to use a for/in NSFastEnumeration loop instead.
    NSDate *dt5 = [NSDate date];

    NSLog(@"Time taken to create the data array %f",[dt2 timeIntervalSinceDate:dt1]);
    NSLog(@"Time taken to iterate using normal for loop %f",[dt3 timeIntervalSinceDate:dt2]);
    NSLog(@"Time taken to iterate using fast enumeration %f",[dt4 timeIntervalSinceDate:dt3]);
    NSLog(@"Time taken to iterate using blocks %f",[dt5 timeIntervalSinceDate:dt4]);

    //Time taken to create the data array 0.383750
    //Time taken to iterate using normal for loop 0.309719
    //Time taken to iterate using fast enumeration 0.278467
    //Time taken to iterate using blocks 0.526629   //Blocks are taking comparatively more time to iterate

/* END PERFORMANCE TEST 2*/

/*BEGIN ENUMERATION OPTIONS */
    NSMutableArray *mArray4 = [[[NSMutableArray alloc] init] autorelease];
    [dataArray enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        if ([obj intValue] == 9999000)
        {
            *stop = YES; //This is going to stop the enumeration
            return;
        }
        [mArray4 addObject:obj];
    }];
    NSLog(@"mArray count%d",[mArray4 count]); //count = 999
    NSLog(@"mArray objectAtIndex 0 %d",[[mArray4 objectAtIndex:0] intValue]);    //mArray objectAtIndex 0 9999999
    //The above method iterates in the reverse order
    //The two options available are  NSEnumerationConcurrent  and NSEnumerationReverse

/*END ENUMERATION OPTIONS */
}

/*
BEGIN USE CASE FOR BLOCK (Show/hide progress indicator while a task is being executed)
*/
/* 
 A simple block implementation to show a progress indicator while a task is in progress.
 Once the task is completed the progress indicator needs to hide.
*/

//The order of execution is as follows
//-[KSAppDelegate getDataFromDatabase:]
//-[KSAppDelegate showProgressIndicator]
//-[KSAppDelegate doActualDatabringingActivity:]
//-[KSAppDelegate hideProgressIndicator]

-(IBAction)getDataFromDatabase:(id)sender
{
    NSLog(@"%s",__func__);
    [self showProgressIndicator];
    [self doActualDatabringingActivity:^{
        [self hideProgressIndicator];
        /*
         Avoid Strong reference cycles when capturing self inside blocks
         Capturing a strong reference to self creates a strong reference cycle.
         //To avoid this, its better to capture a wek reference to self

         InterfaceName *__weak weakSelf = self;
         [weakSelf doSomething];
         */
    }];
}

-(void)doActualDatabringingActivity:(void (^) (void))callBackBlock
{
    NSLog(@"%s",__func__);
    callBackBlock();
}

-(void)showProgressIndicator
{
    NSLog(@"%s",__func__);
}

-(void)hideProgressIndicator
{
    NSLog(@"%s",__func__);
}

/*END BLOCK USE CASE*/

/*BEGIN OTHER METHODS */
-(int)addTwoInts:(int)a andB:(int)b{ //A simple add method to test performance against blocks
        return a+b;
}

//This is a global block that adds two ints and returns the result
int (^additionBlock)(int,int) = ^(int a, int b){
    return a+b;
};

// A method that returns a completion block
-(void)countToHundredAndReturnCompletionBLock:(void (^)(BOOL))completed
{
    int x = 1;
    while (x < 100) {
        x++;
    }
//  completed(NO);
    completed(YES);
}

// A method that returns a completion block
-(void)doSomething:(EndBlock)completionBlock
{
    NSLog(@"Do Something first");
    //This can be a lengthy calculation here. Once this is done then the completion block is called
    completionBlock();
    NSLog(@"Then may be something else");
}

void (^aGlobalBlock)(void) = ^{
    NSLog(@"This is a global block which does not take any input parameter and does not return any thing");
};
/*END OTHER METHODS */
@end

Download the Source code for ObjcBlocks Sample Code

Posted in Cocoa, iOS, Objective C Tagged with: ,
4 comments on “Blocks in Objective-C
  1. Debasis Das says:

    Thanks Abe, I am glad this of use to you.

  2. Good post. I am going through some of these issues as well..

  3. lingam says:

    kindly include the download option on this post..

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Recent Posts


Hit Counter provided by technology news