⬆️ ⬇️

About blocks and their use in Objective-C part 1

In OS X 10.6 and iOS 4.0, Apple announced support for blocks that are essentially closures . Further about blocks in a development context under IOS, Objective-C (that is work without gc ).

To use IOS blocks ver. <4.0 you can apply ESBlockRuntime or PLBlocks .



Briefly about the theory



The block instance, the block type, and the block literal itself are denoted using the ^ operator, for example:



typedef int (^MyBlock)( int );



int multiplier = 7;

MyBlock myBlock = ^( int num) {

return num * multiplier;

};




* This source code was highlighted with Source Code Highlighter .


or

')

int multiplier = 7;

int (^myBlock)( int ) = ^( int num) {

return num * multiplier;

};




* This source code was highlighted with Source Code Highlighter .


Calling a block is similar to calling a regular sishnaya function. For example:



myBlock( 3 )



* This source code was highlighted with Source Code Highlighter .


The main feature of the blocks is their ability to store the context in which they were created. In the example above, “myBlock” will always multiply the number by 7. How does all this work?



Kinds of block context variables


1. Primitive types C and structures, blocks stored as constants. Example:





int multiplier = 7;

int (^myBlock)( int ) = ^( int num) {

return num * multiplier;

};

multiplier = 8;

NSLog( @"%d" , myBlock( 3 ) );




* This source code was highlighted with Source Code Highlighter .


Prints - 21, not 24.



2. Variables set with the __block keyword are mutable. This works by copying the value of such a variable into a heap, and each block stores a reference to this variable. Example:



__block int multiplier = 7;

int (^myBlock)( int ) = ^( int num) {

return num * multiplier;

};

multiplier = 8;

NSLog( @"%d" , myBlock( 3 ) );




* This source code was highlighted with Source Code Highlighter .


Prints - 24, not 21.



3. Variables - pointers to objects with reference counting (id, NSObject). For them, retain is called when copying a block to a heap. Example:



NSDate* date = [ [ NSDate alloc ] init ];



void (^printDate)() = ^() {

NSLog( @"date: %@" , date );

};



//

printDate = [ [ printDate copy ] autorelease ];



[ date release ];



printDate();




* This source code was highlighted with Source Code Highlighter .


Here you want to draw your attention to the fact that the retain of the date object occurs during the copying of the block into the heap, and not during its creation. For example, this code will fall with “EXC_BAD_ACCESS”



NSDate* date = [ [ NSDate alloc ] init ];



void (^printDate)() = ^() {

NSLog( @"date: %@" , date );

};



[ date release ];



//

printDate = [ [ printDate copy ] autorelease ];



printDate();




* This source code was highlighted with Source Code Highlighter .


4. Variables - pointers to objects with reference counting (id, NSObject) declared with the __block keyword. They are NOT called retain when copying a block to a heap. Example:

__block NSDate* date = [ [ NSDate alloc ] init ];



void (^printDate)() = ^() {

// date

NSLog( @"date: %@" , date );

};



// , date retain

printDate = [ [ printDate copy ] autorelease ];



[ date release ];



printDate();




* This source code was highlighted with Source Code Highlighter .


This is usually used to avoid circular references. Example:

@ interface SomeClass : NSObject



//

@property ( nonatomic, copy ) SimpleBlock block;



@end



@implementation SomeClass



@synthesize block = _block;



-( void )dealloc

{

[ _block release ];



[ super dealloc ];

}



-( void )methodB

{

}



-( void )methodA

{

__block SomeClass* self_ = self;

// ( ) - ,

self.block = ^()

{

// retain self_

[ self_ methodB ];

};

}



@end




* This source code was highlighted with Source Code Highlighter .


The blocks are instances of the NSObject class (specific classes of these objects are not defined), so we can and must use the methods of the NSObject - copy, retain, release and autorelease class methods for blocks. But why do we need it?



Blocks and memory management


By default, block instances are not created on the heap, as one might expect, but on the stack. Therefore, if necessary, make a deferred call block first you need to copy it into a heap.



Suppose there is an extension of the class NSObject with the “performAfterDelay:” method, which performs the specified block with a delay.



@implementation NSObject (BlocksExtensions)



-( void )callSelfBlock

{

void * self_ = self;

ESSimpleBlock block_ = (ESSimpleBlock)self_;

block_();

}



-( void )performAfterDelay:( NSTimeInterval )delay_

{

[ self performSelector: @selector( callSelfBlock ) withObject: nil afterDelay: delay_ ];

}



@end




* This source code was highlighted with Source Code Highlighter .


And, actually, the challenge:

NSDate* date = [ NSDate date ];



void (^printDate)() = ^() {

NSLog( @"date: %@" , date );

};



[ printDate performAfterDelay: 0.3 ];




* This source code was highlighted with Source Code Highlighter .


Such code will “overthrow” our application, because the stack unit will be destroyed by the time it is called, and we will turn to random memory in the place where the unit is called. Although this code:

void (^printDate)() = ^() {

NSLog( @"date: %@" , [ NSDate date ] );

};



[ printDate performAfterDelay: 0.3 ];




* This source code was highlighted with Source Code Highlighter .


will work great. What is the reason? Note that the last block does not refer to external variables, therefore there is no need to create a copy of it. In this case, the compiler creates a so-called Global block. The program has only one instance of such a block, the lifetime of which is limited by the lifetime of the application. Thus, GlobalBlock can be considered as a singletone object.



Types of block variables


And so, to summarize. There are three types of blocks: global (stateless), local or stack, and blocks in the heap (MallocBlock). Therefore, the copy, retain, release, and autorelease methods of the global block do nothing. The retain method also does nothing for the stack block. For the Malloc block, the copy method in turn works like a retain for NSObject.



And of course the revised version of the previous example with the addition of the copy method:

@implementation NSObject (BlocksExtensions)



-( void )callSelfBlock

{

void * self_ = self;

ESSimpleBlock block_ = (ESSimpleBlock)self_;

block_();

}



-( void )performAfterDelay:( NSTimeInterval )delay_

{

// , - afterDelay:

self = [ [ self copy ] autorelease ];

[ self performSelector: @selector( callSelfBlock ) withObject: nil afterDelay: delay_ ];

}



@end




* This source code was highlighted with Source Code Highlighter .


Continued: "On blocks and their use in Objective-C part 2"

Source: https://habr.com/ru/post/119877/



All Articles