
Objective-c blocks and c ++ lambdas
- Tutorial
I hope that the post will be useful to people who are familiar with C ++ lambdas, but want to learn Objective-C blocks and vice versa.
Here I tried to describe the syntax of closures, mechanisms for capturing the context, memory management and the interaction of lambdas and blocks with each other.
All examples used Apple LLVM Compiler 4.2 (Clang). ARC is not used for the Obj-C code, because I am of the opinion that you need to know how non-ARC code works in order to understand how ARC works.
Blocks in Objective-C are an implementation of closures [2] . Blocks are anonymous functions that can capture the context (current stack variables and class member variables). Blocks at runtime are represented by objects; they are analogues of lambda expressions in C ++.
Lambdas in C ++ are also implementation of closures and represent anonymous local functions.
[3]
Blocks have pointer semantics.
Blocks in Objective-C have already found their place in standard frameworks (Foundation, UIKit) and in third-party libraries ( AFNetworking , BlocksKit ).
We give an example in the form of a class category NSArray
First of all, they are great for asynchronous operations, you can verify this using AFNetworking, and working with them in GCD is a pleasure.
We can define our block types, for example:
The same lambda code
[11]
We give a similar example of selecting a subset for a lambda
We can automatically use the values of stack variables in blocks if we do not change them. For example, in the above example, we did not indicate in the block declaration that we want to capture a variable
We simply took its value by the name declared in the body of the function. If we wanted to change its value in the body of the block, we would have to mark the variable with a modifier
In order to send messages to the object, the pointer to which we pass to the block, there is no need to mark the pointer as
But sometimes, nevertheless, it is necessary to mark the pointer to the object with
The captured variables are indicated in a specific place [5] , namely inside the square brackets []
The specifier is related to the capture of context
that you can change copies of variables passed by value. More details in the next section.
Blocks are objects, they are created on the stack (later they can be transferred to a heap).
Blocks can exist in the form of 3 implementations [7] .
Here is an example ownership cycle:
Let's say you wanted to create a class that makes a
And then you wanted to create a specific request for a specific API of your server
The error in this line

You could avoid this by creating an intermediate pointer to self on the stack with the __block modifier, like this:
Alternatively, you can transfer blocks of method signature initialization method
We give another example of incorrect memory management with blocks, an example taken from the video lecture [7]
If we copied the block into a heap and passed up the stack, an error would not have occurred.
Also, this example will not cause errors in the ARC code.
The implementation of lambdas in runtime may be specific in different compilers. They say that memory management for lambdas is not very described in the standard. [9]
Consider a common implementation.
Lambdas in C ++ are objects of an unknown type that are created on the stack.
Lambdas that do not capture any context can be cast to a function pointer, but still this does not mean that they themselves are just pointers to a function. A lambda is an ordinary object, with a constructor and a destructor, standing out on the stack.
Here are some examples of moving lambdas in heap
Now you can pass the function to a member variable of some object.
mutable after declaring lambda arguments, it means that you can change the values of copies of variables captured by value (The value of the original variable will not change). For example, if we defined a lambda like this:
Since Objecitve-C ++ combines both Objective-C and C ++, you can use lambdas and blocks at the same time. How do lambdas and blocks relate to each other?
It should be said that the operations between lambdas and blocks are quite exotic, for example, I have never seen such assignments in projects.
Here I tried to describe the syntax of closures, mechanisms for capturing the context, memory management and the interaction of lambdas and blocks with each other.
All examples used Apple LLVM Compiler 4.2 (Clang). ARC is not used for the Obj-C code, because I am of the opinion that you need to know how non-ARC code works in order to understand how ARC works.
Sections:
Blocks in Objective-C are an implementation of closures [2] . Blocks are anonymous functions that can capture the context (current stack variables and class member variables). Blocks at runtime are represented by objects; they are analogues of lambda expressions in C ++.
Lambdas in C ++ are also implementation of closures and represent anonymous local functions.
Syntax
Obj-C blocks

In text form
int multiplier = 7;
int (^myBlock)(int) = ^(int num) { return num * multiplier;};
NSLog(@”%d”, myBlock(4)); // выведет 28
- ^ - this symbol tells the compiler that the variable is a block
- int - the block accepts one parameter of type int, and returns a parameter of type int
- multiplier - a variable that comes to us from the context (more about this in the section “Capturing the context”)
Blocks have pointer semantics.
Blocks in Objective-C have already found their place in standard frameworks (Foundation, UIKit) and in third-party libraries ( AFNetworking , BlocksKit ).
We give an example in the form of a class category NSArray
Block Usage Example in NSArray
// имплементация категории
@implementation NSArray (Blocks)
// метод возвращает массив, элементы которого соответствуют предикату
- (NSArray*)subarrayWithPredicate:(BOOL(^)(id object, NSUInteger idx, BOOL *stop))predicte {
NSMutableArray *resultArray = [NSMutableArray array];
BOOL shouldStop = NO;
for (id object in self) {
if (predicte(object, [self indexOfObjectIdenticalTo:object], &shouldStop)) {
[resultArray addObject:object];
}
if (shouldStop) {
break;
}
}
return [[resultArray copy] autorelease];
}
@end
// где-то в клиентском коде
NSArray *numbers = @[@(5), @(3), @(8), @(9), @(2)];
NSUInteger divisor = 3;
NSArray *divisibleArray = [numbers subarrayWithPredicate:^BOOL(id object, NSUInteger idx, BOOL *stop) {
BOOL shouldAdd = NO;
// нам нужны числа кратные 3
NSAssert([object isKindOfClass:[NSNumber class]], @"object != number");
// обратим внимание, что переменную divisor мы взяли из контекста
if ([(NSNumber *)object intValue] % divisor == 0) {
shouldAdd = YES;
}
return shouldAdd;
}];
NSLog(@"%@", divisibleArray); // выведет 3, 9
First of all, they are great for asynchronous operations, you can verify this using AFNetworking, and working with them in GCD is a pleasure.
We can define our block types, for example:
Block Type Declaration
typedef int (^MyBlockType)(int number, id object);
C ++ lambdas
The same lambda code

In text form
int multiplier = 7;
auto lambda = [&multiplier](int num) throw() -> int
{
return multiplier * num;
};
lambda(4); // равно 28
[]
- start of lambda declaration, inside - context capture&multiplier
- captured variable (&
means captured by reference)int
- input parametermutable
- a keyword that allows you to modify the variables captured by valuethrow()
- indicates that lambda does not throw any exceptions out
We give a similar example of selecting a subset for a lambda
Retrieve a subset from a predicate collection
template
void subset(InputCollection& inputCollection, InputCollection& outputCollection, UnaryPredicate predicate)
{
typename InputCollection::iterator iterator = inputCollection.begin();
for (;iterator != inputCollection.end(); ++iterator) {
if (predicate(*iterator)) {
outputCollection.push_back(*iterator);
}
}
return;
}
int main(int argc, const char * argv[])
{
int divisor = 3;
std::vector inputVector = {5, 3, 8, 9, 2};
std::vector outputVector;
subset(inputVector, outputVector, [divisor](int number){return number % divisor == 0;});
// выводим значения полученной коллекции
std::for_each( outputVector.begin(),
outputVector.end(),
[](const int& number){std::cout << number << std::endl;} );
}
Context capture
Obj-C blocks
We can automatically use the values of stack variables in blocks if we do not change them. For example, in the above example, we did not indicate in the block declaration that we want to capture a variable
multiplier
(unlike a lambda, in a lambda we could specify [&]
to capture the entire context by reference, or [=]
to capture the entire context by value). We simply took its value by the name declared in the body of the function. If we wanted to change its value in the body of the block, we would have to mark the variable with a modifier
__block
Example of changing a variable value from context
__block int first = 7;
void (^myBlock2)(int) = ^(int second) { first += second;};
myBlock2(4);
NSLog(@"%d", first); // выведет 11
In order to send messages to the object, the pointer to which we pass to the block, there is no need to mark the pointer as
__block
not. Indeed, in fact, when we send a message to an object, we do not change its pointer.An example of sending a message to an object from context
NSMutableArray *array = [NSMutableArray array];
void (^myBlock3)() = ^() { [array addObject:@"someString"];};
myBlock3(); // добавит someString в array
But sometimes, nevertheless, it is necessary to mark the pointer to the object with
__block
, to avoid memory leaks. (More on this in the section “Memory Management”)C ++ lambdas
The captured variables are indicated in a specific place [5] , namely inside the square brackets []
[&]
- means that we capture all the characters on the link[=]
- all characters by value[a, &b]
-a
captured by value,b
captured by reference[]
- nothing is captured
The specifier is related to the capture of context
mutable
, it says that you can change copies of variables passed by value. More details in the next section.
Memory management
Obj-C blocks
Blocks are objects, they are created on the stack (later they can be transferred to a heap).
Blocks can exist in the form of 3 implementations [7] .
- When we do not use variables from the context (from the stack) inside the block, it is created
NSGlobalBlock
, which is implemented as a singleton. - If we use context variables, then it is created
NSStackBlock
, which is no longer a singleton, but is located on the stack. - If we use a function
Block_copy
, or we want our block to be stored inside some object placed in a heap, for example, as a property of an object:@property (nonatomic, copy) MyBlockType myBlock;
then an object of a class is createdNSMallocBlock
that captures and masters (masters == sends a messageretain
) objects transferred in context. This is a very important property, because it can lead to memory leaks if it is not handled carefully. Blocks can create retain cycles. It is also important to note that if we use the valueproperty
inNSMallocBlock
- it will not be the property itself that will retain, but the object to which the property belongs.
Here is an example ownership cycle:
Let's say you wanted to create a class that makes a
HTTP
request with asynchronousAPI PKHTTPReuquest
PKHTTPReuquest implementation
typedef void (^PKHTTPRequestCompletionSuccessBlock)(NSString *responseString);
typedef void (^PKHTTPRequestCompletionFailBlock)(NSError* error);
@protocol PKRequest
- (void)startRequest;
@end
@interface PKHTTPRequest : NSObject
// designated initializer
- (id)initWithURL:(NSURL *)url
successBlock:(PKHTTPRequestCompletionSuccessBlock)success
failBlock:(PKHTTPRequestCompletionFailBlock)fail;
@end
@interface PKHTTPRequest ()
@property (nonatomic, copy) PKHTTPRequestCompletionSuccessBlock succesBlock;
@property (nonatomic, copy) PKHTTPRequestCompletionFailBlock failBlock;
@property (nonatomic, retain) NSURL *url;
@property (nonatomic, retain) NSURLConnection *connection;
@property (nonatomic, retain) NSMutableData *data;
@end
@implementation PKHTTPRequest
#pragma mark - initialization / deallocation
// designated initializer
- (id)initWithURL:(NSURL *)url
successBlock:(PKHTTPRequestCompletionSuccessBlock)success
failBlock:(PKHTTPRequestCompletionFailBlock)fail {
self = [super init];
if (self != nil) {
self.succesBlock = success;
self.failBlock = fail;
self.url = url;
NSURLRequest *request = [NSURLRequest requestWithURL:self.url];
self.connection = [[[NSURLConnection alloc] initWithRequest:request
delegate:self
startImmediately:NO] autorelease];
}
return self;
}
- (id)init {
NSAssert(NO, @"Use desiganted initializer");
return nil;
}
- (void)dealloc {
self.succesBlock = nil;
self.failBlock = nil;
self.url = nil;
self.connection = nil;
self.data = nil;
[super dealloc];
}
#pragma mark - public methods
- (void)startRequest {
self.data = [NSMutableData data];
[self.connection start];
}
#pragma mark - NSURLConnectionDelegate implementation
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.data appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
self.failBlock(error);
self.data = nil;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
self.succesBlock([NSString stringWithUTF8String:self.data.bytes]);
self.data = nil;
}
@end
And then you wanted to create a specific request for a specific API of your server
PKGetUserNameRequest
that works withPKHTTPReuquest
PKGetUserNameRequest implementation
typedef void (^PKGetUserNameRequestCompletionSuccessBlock)(NSString *userName);
typedef void (^PKGetUserNameRequestCompletionFailBlock)(NSError* error);
@interface PKGetUserNameRequest : NSObject
- (id)initWithUserID:(NSString *)userID
successBlock:(PKGetUserNameRequestCompletionSuccessBlock)success
failBlock:(PKGetUserNameRequestCompletionFailBlock)fail;
@end
NSString *kApiHost = @"http://someApiHost.com";
NSString *kUserNameApiKey = @"username";
@interface PKGetUserNameRequest ()
@property (nonatomic, retain) PKHTTPRequest *httpRequest;
- (NSString *)parseResponse:(NSString *)response;
@end
@implementation PKGetUserNameRequest
#pragma mark - initialization / deallocation
- (id)initWithUserID:(NSString *)userID
successBlock:(PKGetUserNameRequestCompletionSuccessBlock)success
failBlock:(PKGetUserNameRequestCompletionFailBlock)fail {
self = [super init];
if (self != nil) {
NSString *requestString = [kApiHost stringByAppendingFormat:@"?%@=%@", kUserNameApiKey, userID];
self.httpRequest = [[[PKHTTPRequest alloc] initWithURL:[NSURL URLWithString:requestString]
successBlock:^(NSString *responseString) {
// роковая ошибка - обращение к self
NSString *userName = [self parseResponse:responseString];
success(userName);
} failBlock:^(NSError *error) {
fail(error);
} ] autorelease];
}
return self;
}
- (id)init {
NSAssert(NO, @"Use desiganted initializer");
return nil;
}
- (void)dealloc {
self.httpRequest = nil;
[super dealloc];
}
#pragma mark - public methods
- (void)startRequest {
[self.httpRequest startRequest];
}
#pragma mark - private methods
- (NSString *)parseResponse:(NSString *)response {
/* ...... */
return userName;
}
@end
The error in this line
NSString *userName = [self parseResponse:responseString];
is that when we call something in self in the Malloc block, self resets, the following loop forms in the ownership graph: 
You could avoid this by creating an intermediate pointer to self on the stack with the __block modifier, like this:
Example of breaking a tenure cycle
// разрываем цикл владения
__block PKGetUserNameRequest *selfRequest = self;
self.httpRequest = [[[PKHTTPRequest alloc] initWithURL:[NSURL URLWithString:requestString]
successBlock:^(NSString *responseString) {
NSString *userName = [selfRequest parseResponse:responseString];
success(userName);
} failBlock:^(NSError *error) {
fail(error);
} ] autorelease];
Alternatively, you can transfer blocks of method signature initialization method
startRequest
, startRequestwithCompaltion:fail:
and reteynit blocks only for the duration of the request. Then one could do without a modifier __block
. This would solve another problem: in the above example, there is a danger that by the time the block is called (by the time the request is completed), an object of type PKGetUserNameRequest will cease to exist (the dealloc method will be called), since the block provides weak communication. And selfRequest
zombies will hang on the pointer , which will cause crash. We give another example of incorrect memory management with blocks, an example taken from the video lecture [7]
Error with NSStackBlock
void addBlockToArray(NSMutableArray* array) {
NSString *string = @"example string";
[array addObject:^{
printf("%@\n", string);
}];
}
void example() {
NSMutableArray *array = [NSMutableArray array];
addBlockToArray(array);
void (^block)() = [array objectAtIndex:0];
block();
}
If we copied the block into a heap and passed up the stack, an error would not have occurred.
Also, this example will not cause errors in the ARC code.
C ++ lambdas
The implementation of lambdas in runtime may be specific in different compilers. They say that memory management for lambdas is not very described in the standard. [9]
Consider a common implementation.
Lambdas in C ++ are objects of an unknown type that are created on the stack.
Lambdas that do not capture any context can be cast to a function pointer, but still this does not mean that they themselves are just pointers to a function. A lambda is an ordinary object, with a constructor and a destructor, standing out on the stack.
Here are some examples of moving lambdas in heap
An example of moving a lambda in heap
// Способ №1
auto lamb = []() {return 5;};
auto func_lamb_ptr = new std::function(lamb);
// Способ №2:
auto lamb = []() {return 5;};
auto* p = new decltype(lamb)(lamb);
// Способ №3:
template
T* heap_alloc(T const& value)
{
return new T(value);
}
auto* p = heap_alloc([]() {return 5;});
// Способ №4:
std::vector v;
v.push_back(lamb);
Now you can pass the function to a member variable of some object.
mutable after declaring lambda arguments, it means that you can change the values of copies of variables captured by value (The value of the original variable will not change). For example, if we defined a lambda like this:
auto lambda = [multiplier](int num) throw() mutable
we could change the value multiplier
inside the lambda, but the multipler declared in the function has not changed. Moreover, the changed valuemultiplier
saved from call to call of this lambda instance. You can imagine it this way: in a lambda instance (in an object) variable members are created corresponding to the passed parameters. We need to be careful here, because if we copy an instance of a lambda and call it, then these member variables will not change in the original lambda, they will only change in the copied one. Sometimes you need to pass lambdas wrapped in std::ref
. Obj-C blocks do not provide such an opportunity out of the box.Objective-C ++
Since Objecitve-C ++ combines both Objective-C and C ++, you can use lambdas and blocks at the same time. How do lambdas and blocks relate to each other?
- We can assign a lambda to the block.Example
void (^block_example)(int); auto lambda_example = [](int number){number++; NSLog(@"%d", number);}; block_example = lambda_example; block_example(10); // log 11
- We can assign the block to the std :: function object.
Here it is worth noting that Objective-C and C ++ have different memory management policies, and storing the block instd::function
can lead to “hanging” links. - We cannot assign a lambda block.
Lambda does not have a copy-assignment statement defined. Therefore, we cannot assign to it either a block or even ourselves.Assignment Errorint main() { auto lambda1 = []() -> void { printf("Lambda 1!\n"); }; lambda1 = lambda1; // error: use of deleted function ‘main()::
& main():: ::operator=(const main():: &)’ return 0; }
It should be said that the operations between lambdas and blocks are quite exotic, for example, I have never seen such assignments in projects.
Related Links
- About C ++ Lambdas
- Short circuits
- About Apple Blocks
- Comparison of lambdas and blocks in English
- Docks C ++ Lambdas
- About blocks
- Great video about blocks
- Question about organizing C ++ lambdas memory on stackoverflow.com
- Question about implementing C ++ lambdas in runtime
- About the interaction of lambdas and blocks
- Lambda syntax
- About the interaction of Objective-C and C ++
- Ways to embed C ++ in Objective-C projects