I know that an Objective-C Block can capture and set the value of variables outside of its enclosing scope. How does it do that?
It's actually fairly straightforward and described in Clang's Block Implementation Spec, in the "Imported Variables" section.
When the compiler encounters a Block like:
^{ if( numBalloons > numClowns) abort(); }
it creates a literal structure that includes -- among other things -- two elements that are important here. There's a function pointer to the executable code in the Block, and a const
field for each variable that's referred to inside the Block. Something like this:
struct __block_literal_1 {
/* other fields */
void (*invoke)(struct __block_literal_1 *);
/* ... */
const int numBalloons;
const int numClowns;
};
Notice that the invoke
function will take a pointer to a struct of the kind that's being defined right here; that is, the Block passes itself in when executing its code. Thus, the code gets access to the members of the structure.
Right after the declaration, the compiler creates a definition of the Block, which simply uses the referenced variables to initialize the correct fields in the struct
:
struct __block_literal_1 __block_literal_1 = {
/* Other fields */
__block_invoke_2, /* This function was also created by the compiler. */
/* ... */
numBalloons, /* These two are the exact same variables as */
numClowns /* those referred to in the Block literal that you wrote. *
};
Then, inside the invoke
function, references to the captured variables are made like any other member of a struct, the_block->numBalloons
.
The situation for object-type variables is a little more complicated, but the same principle applies.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With