Blocks
Blocks are executable code just like a function. It can be written inside a function or we can store the reference in a variable and call it later. But wait a minute these all functionality we can achieve by function pointer and functions.
Then what is so special about blocks? Let first go through how we can define blocks.
The above block can be used to find the sum of two numbers. If we recall the function pointer in c then it is very much similar to above:
int (^sum)(int, int) = ^(int number1, int number2){ return number1+number2; };
The above block can be used to find the sum of two numbers. If we recall the function pointer in c then it is very much similar to above:
int sumFunction(int number1, int number2){ return number1+number2; } int (*sum) (int, int) = sumFunction; //Function Pointer *sum
Then Why we need Blocks if we already have Function pointer?
1) Blocks can capture variables from the enclosing scope by simply referring to them within the block. How ? Let see the next Section
2) Blocks are Objective-c Objects not like a function pointer (reference to executable code). So Blocks stores much more information. What information ? Let see the next section
Any Other Difference !!!!
Yes there is one more point specially for technical Interview! block is define with (^) sign and function pointer with (*) :) .
Let's Check How Compiler Handles Blocks?
Like any other c code blocks also has two segment:
1) Code segment (text segment)
1) Code segment (text segment)
2) Data Segment (Block Literal)
In the above case calculator definition will reside at different location and "Sum and Subtract" definition will be at different place after compilation.
Data Segment (Block Literal):
We can see there is nothing special in code segment by which we can say blocks are different from the function and function pointer. So there is structure called Block Literal which make is special. The data structure store the information regarding the variable used from the outer scope and memory information regarding the blocks.
This data structure is generated by the Clang while compilation. So we do not need to worry about that. You just need to create a block and use it.
More Detail:
Code segment:
Complied block code reside in different location not with in the code it actually define.- (void) calculator{ int (^sum) (int, int) = ^(int number1, int number2){ return number1+number2; }; int (^subtract)(int, int) = ^(int number1, int number2){ return number1-number2; }; }
In the above case calculator definition will reside at different location and "Sum and Subtract" definition will be at different place after compilation.
Data Segment (Block Literal):
We can see there is nothing special in code segment by which we can say blocks are different from the function and function pointer. So there is structure called Block Literal which make is special. The data structure store the information regarding the variable used from the outer scope and memory information regarding the blocks.
This data structure is generated by the Clang while compilation. So we do not need to worry about that. You just need to create a block and use it.
More Detail:
Blocks Stack To Heap Travel:
Blocks are objective-c objects. If you have gone through the above link you find in the block data structure a member with name 'isa'. We already know with each object in objective-c we have this variable which used to find the class of the object.
Question is if Block is object then there must be a/are class/classes for block?
Let find the class for blocks by run the following code:
Now try the same code with ARC off or No.
Now try the following code:
So now we have three different classes for block. Point is how to know before run the code from which class my block belongs?
2) NSStackBlock
3) NSGlobalBlock
Just Try the following code:
So blocks are different from the other objective-C object but still they are objective-c Object. Block also copy the local variable onto the stack which you have used from the surrounding scope. So Even if surrounding scope variable disposed after the function (capturing the block) is removed from the stack a copy of ivar is there in the stack for the block reference.
Question is if Block is object then there must be a/are class/classes for block?
Let find the class for blocks by run the following code:
NSDate *estDate = [NSDate date]; NSLog(@"%@",estDate); void (^sum) (int number1, int number2) = ^(int number1, int number2){ NSLog(@"sum is %@", estDate); }; NSLog(@"sum is of type %@", sum);
Output:
sum is of type ;__NSMallocblock__0x7fd50863f850;
Now try the same code with ARC off or No.
Output: sum is of type ;__NSStackBlock__: 0x7fff560059e8;
void (^sum) (int number1, int number2) = ^(int number1, int number2){ NSLog(@"sum is %d", number1+number2); }; NSLog(@"sum is of type %@", sum);
Output: sum is of type <__NSGlobalBlock__: 0x10256b0b0;
So now we have three different classes for block. Point is how to know before run the code from which class my block belongs?
Class of a block is depend upon the scope and memory segment in which it placed at run time:
1) NSMallocBlock2) NSStackBlock
3) NSGlobalBlock
NSStackBlock:
Default when you create a block and Block Literal is placed on the stack and the class for the block is NSStackBlock. If you ever tried any objective-c object to placed on stack then it will give you the compilation error:Just Try the following code:
NSString test = [[NSString alloc] init];
Error:
Interface type cannot be statically allocated
So blocks are different from the other objective-C object but still they are objective-c Object. Block also copy the local variable onto the stack which you have used from the surrounding scope. So Even if surrounding scope variable disposed after the function (capturing the block) is removed from the stack a copy of ivar is there in the stack for the block reference.
NSGlobalBlock:
In the above scenario if any block do not use the surrounding scope variable or object then clang configure it for the global scope. The class refer by the block is NSGlobalBlock. In the above example you can see if i am not using any surrounding Scope then in output it return NSGlobalBlock.
So block in this case is global and placed in global scope. So it will never be copied and disposed in this case.
NSMallocBlock:
We know one of the functionality given by Base class (NSObject) in Objective-c is methods for memory management (retain, copy, release). Blocks are objective-c Object so these methods are also applicable on to the blocks.
When you copy a block which placed on the stack then a new memory is allocated on heap and all the code segment and data segment moved to the Heap. So new memory reference for all the block litreal member will be there when you copy a block from the stack to heap. Class denotes by block in this case is NSMallocBlock.
In case of ARC even if you define a block in a function it automatically has NSMallocBlock reference. i.e. ARC automatically send the copy message during compilation.
Memory points to take care(Be Safe):
Till know we are only talking about local variable is copied if used in a block. What happen if i refer any objective-C object in a block and the object disposed before the block called?
Answer is block retain the object when we send a copy message to a block. So the reference count of the object will increase when you copy the block. So we can use it without any harm. If we are giving any flexibility to a programmer then it come with the cost. Cost here is you need to take care of retain Cycle. How?
Retain Cycle:
typedef void (^ButtonTap)(id sender); @interface EventHandling : NSObject @property (nonatomic, copy) ButtonTap tapEvent; @end @implementation EventHandling - (id)init { if ((self = [super init])) { self.tapEvent = ^(id sender){ NSLog(@"object is %@", self); // self retain cycle }; } return self; } - (void)dealloc { self.tapEvent = nil; NSLog (@"block free"); // never called. [super dealloc]; } @end
In the above case TapEvent and Event Handling object never dealloc. As both retain each other.
Block Mysteries:
Stack Mystery (Non-ARC):
As we know block default created on a stack. So if you send a retain message to a block on stack(NSStackBlock) then it does not have any effect. Its reference count will not be increase. We already discussed Blocks can use variable from local scope. Let suppose if you are using an int variable from the function local variable and function is removed from the stack. Can we still have the same value in scaler variable ? Yes
All the variable used from the enclosing scope is const copy with in block data Structure (Block Literal).
Just Try to run the following code with Non-ARC:
int numberSum = 0; NSLog(@"number sum address %p", &numberSum); void (^sum) (int number1, int number2) = ^(int number1, int number2){ NSLog(@"number sum address %p", &numberSum); }; sum(1, 2);
Output: number sum address 0x7fff5a75e9cc number sum address 0x7fdb90ca6930 (Block on Stack)
Memory Address of the variable is changed.
Remember it is const copy you cannot change it. If you wanna change the value of variable just stay tune for next part of tutorial.
Heap Mystery:
You need to copy a block if you want to increase the scope or return a block from the method. When you send a copy message to a block then Block Literal also copied onto heap. Retain count of all the object used in block will also increase by one.
Just Try to run the following code with Non-ARC Environment:
int numberSum = 0; NSLog(@"number sum address enclosing Scope %p", &numberSum); void (^sum) (int number1, int number2) = ^(int number1, int number2){ NSLog(@"number sum address in block %p", &numberSum); }; sum(1, 2); sum = [sum copy]; sum(3, 4); sum = [sum copy]; sum(5, 6);
Output: number sum address enclosing Scope 0x7fff5ad15a0c number sum address in block 0x7fff5ad159f8 (Block on Stack) number sum address in block 0x7fbe2b5c68e0 (Block on Heap) number sum address in block 0x7fbe2b5c68e0 (Block on Heap)
From the above result we can see if block is move from stack to heap then variable address is also changed. There is one more thing to notice when we send a copy message to block which is already on a heap then memory address is not changed.
Reason is when you send a copy message to a block on a heap then it will not allocate a new memory for the block it just increase the retain count of the block. It is optimization as scope of the block will not be change once it is created.
Pictorial View of Block Memory Allocation Cycle:
Execution Step 2:
Execution Step 3:
Comments
Post a Comment