2
votes

In Objective-C we know that blocks have 3 implementations in runtime.

  1. NSGlobalBlock - that is singleton in runtime, and it is created in case we don't use values of stack variables.
  2. NSStackBlock - that is not singleton, and it is allocated on stack (not on heap), and it is created when we use some stack variables.
  3. NSMallocBlock - that is allocated on heap, and that is used when we want to store Blocks as ivar or property of some Class, or anywhere in heap f.e. @property (nonatomic, copy) MyBlockType myBlock; or when we use Block_copy() function. It is really important because NSMallocBlock retains objects from context, and this fact can create some owning cycles, if we do not use blocks correct.

So, my question is: "Where can I find the full explanation C++ lambdas runtime, and how they are processed by Compiler? Or could you explain that? Is there any specific issues with memory management using C++ lambdas? Where lambdas are allocated, on heap or on stack?"

1
"and it is created in case we don't change values of stack variables using __block modifier." No. NSGlobalBlock is for blocks that are not closures, i.e. that don't capture any local variables from an outside scope. "and it is created when we use _block modifiers for some stack variables." No. Again, it is for any blocks that are closures, i.e. that use a local variable from an outside scope. It doesn't have to a __block variable.newacct
@newacct you are right, NSGlobalBlock just can use static variables, thank you!BergP

1 Answers

2
votes

Lambdas implementation is compiler-specific.

The standard doesn't specify where it is allocated memory-wise, but as a rule of thumb, they are roughly equivalent to old-school functors and they and their captured values are copied on the stack, like any normal object.

E.g.

std::vector<int> v{10,11,12,13};
std::for_each(v.begin(), v.end(), [](int& i) {i++;});

would be just a stateless function.

Or,

std::vector<int> v{10,11,12,13};
int C = 10;
int D = 20;
std::for_each(v.begin(), v.end(), [C,&D](int& i) {i += C + D;});

would be equivalent to a functor constructed on the stack with a copy of C, and a reference to D as its members (but likely would be optimised away).

The only time you really place a lambda object on the heap is when it's converted to std::function, How it's done is dependent on compiler's implementation of std::function.

void g(const std::function& f);
// ...
auto f = [=](int& i){i += C;}; // still on the stack (compiler-specific type)
g(f); // std::function constructed, possibly on the heap