tl;dr
The compiler directly translates block literals into structs and functions. That's why you don't see an alloc
call.
discussion
While blocks are full-fledged Objective-C objects, this fact is seldom exposed in their use, making them quite funny beasts.
One first quirk is that blocks are generally created on the stack (unless they are global blocks, i.e. blocks with no reference to the surrounding context) and then moved on the heap only if needed. To this day, they are the only Objective-C objects that can be allocated on the stack.
Probably due to this weirdness in their allocation, the language designers decided to allow block creation exclusively through block literals (i.e. using the ^
operator).
In this way the compiler is in complete control of block allocation.
As explained in the clang specification, the compiler will automatically generate two structs and at least one function for each block literal it encounters:
- a block literal struct
- a block descriptor struct
- a block invoke function
For instance for the literal
^ { printf("hello world\n"); }
on a 32-bit system the compiler will produce the following
struct __block_literal_1 {
void *isa;
int flags;
int reserved;
void (*invoke)(struct __block_literal_1 *);
struct __block_descriptor_1 *descriptor;
};
void __block_invoke_1(struct __block_literal_1 *_block) {
printf("hello world\n");
}
static struct __block_descriptor_1 {
unsigned long int reserved;
unsigned long int Block_size;
} __block_descriptor_1 = { 0, sizeof(struct __block_literal_1), __block_invoke_1 };
(by the way, that block qualifies as global block, so it will be created at a fixed location in memory)
So blocks are Objective-C objects, but in a low-level fashion: they are just structs with an isa
pointer. Although from a formal point of view they are instances of a concrete subclass of NSBlock
, the Objective-C API is never used for allocation, so that's why you don't see an alloc
call: literals are directly translated into structs by the compiler.