Saturday, May 23, 2015

Cocoa Blocks Quick Reference

Cocoa Blocks are of the most powerful feature but due its syntax many find it difficult to understand. I am trying to provide some examples by which they can learn. Hope this post will help!



Apple Documentation is always the best for reference, but I personally feel in few topics it is bit difficult to understand each and everything. Once you are aware of that then no other reference can match and provide better description than Apple's Documentation.




Introduction: What are Blocks?

Blocks are new feature added to Objective-C and Objective-C++, basically taken from the C-Language. Its just like pointer to a function in C. 


How to write a Block?

1. Start with a ^ (caret symbol) then  { }; Your code should look like this:
^{};

2. You can put few "Enter" between these braces, that will look like what you have seen in many codes.
^{

};

3. Put some code inside here, any Objective-C code.

^{
    NSLog(@"Inside a block");
};

Here you go, yours block is done, and its compiling!!!

But you will see a warning "Expression Result Unused", since this block is never used.

How to access a Block?

Now you have your own block, but how to access it and use it. For this you should  provide a name to the block.

Put a name and assign the above created block to it.

void (^myBlock)(void) = ^{
    NSLog(@"Inside a block");
};

This has four parts, lets see one by one:
 a. The first "void" is not inside a parenthesis "( & )" since this a C-feature, not an Objective-C's. This tells that the assigned block will return "void".

b. "(^myBlock)" is actually the name of the block, you will refer the block by this name. "^" reminds that this is not a method or function but it is a block.

c. Second "void" specifies that this method is taking arguments of type void.

d. "=" assigns the block to the name.

Now you are ready to use the block as can be accessed by :
        myBlock();

Passing arguments and returning values from Block.

In the above example the argument is void hence we've skipped one "void" from the block definition. We could have written it as (which is pretty similar, and will help to understand how to accept arguments)

void (^myBlock)(void) = ^(void){
    NSLog(@"Inside a block");
};

Call the above block by using this statement myBlock();
So far so good?

Now here comes other 3 types of blocks (if  argument and return types are void or some datatypes)

  • Takes one argument as int value:

void (^voidIntBlock)(int) = ^(int arg){
    NSLog(@"You passed: %d",arg);
};

The above block is called by voidIntBlock(6);

  • Takes no argument (void) and returns int value:

int (^intVoidBlock)(void) = ^(void){
    return 420;
};

This one can be called by intVoidBlock(); as used below:

 int returnedInt = intVoidBlock();
 NSLog(@"Returned Int = %d", returnedInt);


  • Takes one int argument and returns int value:

int (^intIntBlock)(int) = ^(int arg){
    return arg*2;
};

The above one by any of these:
intIntBlock(4); 
int returnByTwicing = intIntBlock(4);
NSLog(@"Twiced: %d", returnByTwicing);

What if we need to pass multiple parameters:

int (^sumIntInt)(int, int) = ^(int a, int b){
    return a+b;
};

This one by:
NSLog(@"Sum = %d", sumIntInt(6, 8));
        


Fancy stuffs with Blocks - that creates confusion to beginners.

Our first example can also be written by breaking it into two parts i.Declaration & ii.Definition

 void (^myBlock)(void);
        
  myBlock = ^(void){
        NSLog(@"AA");
  };

This is similar as as create a normal variable :
int num;
num = 11;


Typedefs - makes block simple to read - unless you know how to read it!

    typedef void (^myBlock)(void);        
        
         myBlock TDBlock = ^(void){
            NSLog(@"AA");
     };
        
     TDBlock();

So you come to know, how to use typdefs, but in the above example not much difference, isn't it. But it surely helps when dealing with blocks as a variable.

Lifetime and Scope of variables

In the following example I am using myName inside the block, and it prints the the assigned value. Not to surprising.

NSString *myName = @"Anoop";
        
NSString* (^myBlock)(NSString *);        
myBlock = ^(NSString *str){
NSLog(@"My name : %@ -> %@",myName, str);
             return @"hi block";
};
        
NSLog(@"%@",myBlock(@"anoop"));

Now change the myName to some other value as myName = @"Anoop Vaidya";
 and check the logs, the value still prints the same.

Hence the conclusion: Block doesn't change the value of variable(defined outside) after it is seen in the code. Actually block creates a copy of the initial value once the block is encountered. After the block is executed and no matter what you put the value the initial value(just before the block) is used.

How to update the value in the Block?

Objective-C provides a keyword __block (double underscores block) that is for this particular requirement.

In the above example replace first statement by 

 __block NSString *myName = @"Anoop"; 
NSString* (^myBlock)(NSString *);
        
 myBlock = ^(NSString *str){
 NSLog(@"My name : %@ -> %@",myName, str);
             return @"hi block";
 };
        
 myName = @"Anoop Vaidya";
        
 NSLog(@"%@",myBlock(@"anoop"));
        
Here the new value is reflected in the block.

What if you try to access the outside variable inside the block?

No, you can not. Compiler will slap with an error. But it asks you to use __block to use. Lets see this with a working code snippet. Here Anoop gets replaced by John.

__block NSString *myName = @"Anoop";
NSString* (^myBlock)(NSString *);
        
myBlock = ^(NSString *str){
             myName = @"John";
             NSLog(@"My name : %@ -> %@",myName, str);
             return @"hi block";
};
        

NSLog(@"%@",myBlock(@"anoop"));

Passing blocks as arguments to methods:

For make it simpler and real-time feel, I have created a class and defined four method (based on argument and return type int or void).

@implementation MyClass

-(void)voidVoidCompletionHandler:(void (^)(void))block{
    NSLog(@"void - void");
    block();
}

-(void)voidIntCompletionHandler:(void (^)(int arg))block{
    NSLog(@"void - int");
    block(200);
}

-(void)intVoidCompletionHandler:(int (^)(void))block{
    NSLog(@"int(%d) - void", block());
}

-(void)intIntCompletionHandler:(int (^)(int))block{
    NSLog(@"int(%d) - int", block(400));
}

@end

Call these method one by one as check out for the output, it is self explanatory.

MyClass *myClassObj = [MyClass new];
        
 [myClassObj voidVoidCompletionHandler:^{
            NSLog(@"vv Returned...");
 }];
        
        
  [myClassObj voidIntCompletionHandler:^(int arg) {
            NSLog(@"vi Returned...arg:%d", arg);
  }];
        
        
  [myClassObj intVoidCompletionHandler:^int{
            NSLog(@"iv Returned...");
            return 30000000;
  }];
        
        
  [myClassObj intIntCompletionHandler:^int(int arg) {
            NSLog(@"ii Returned...arg:%d", arg);
            return 40000000;
  }];

The output :

void - void
vv Returned...

void - int
vi Returned...arg:200

iv Returned...
int(30000000) - void

ii Returned...arg:400
int(40000000) - int

Now you come to know when the block gets called and the value is returned and arguments are updated.

Using typedef to shorten the method name:


typedef void (^vv)(void);
-(void)voidVoidCompletionHandler:(vv)block;

typedef void (^vi)(int);
-(void)voidIntCompletionHandler:(vi)block;

typedef int (^iv)(void);
-(void)intVoidCompletionHandler:(iv)block;

typedef int (^ii)(int);
-(void)intIntCompletionHandler:(ii)block;

Looks readable and quite easy. In the above case it has only one argument but sometimes it might contain multiple, then it becomes  difficult to use. Typedef comes into play.

You can replace the same typedef even in .m file. 
---

More on this topic coming soon...