Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How are NSBlock objects created?

I will preface this question by stating that what I am about to ask is for educational and possibly debug purposes only.

How are block objects created internally in the Objective C runtime?

I see the hierarchy of classes that all represent various block types, and the highest superclass in the hierarchy, below NSObject, is NSBlock. Dumping for class data shows that it implements the + alloc, + allocWithZone:, + copy and + copyWithZone: methods. None of the other block subclasses implement these class methods, which leads me to believe, perhaps mistakenly, that NSBlock is responsible for block handling.

But these methods seem not to be called at any point during a block's life time. I exchanged implementations with my own and put a breakpoint in each, but they never get called. Doing similar exercise with NSObject's implementations gives me exactly what I want.

So I assume blocks are implemented in a different manner? Anyone can shed a light on how this implementation works? Even if I cannot hook into the allocation and copying of blocks, I would like to understand the internal implementation.

like image 316
Léo Natan Avatar asked Nov 22 '13 00:11

Léo Natan


People also ask

What is an Objective-C block?

Blocks are a language-level feature added to C, Objective-C and C++, which allow you to create distinct segments of code that can be passed around to methods or functions as if they were values. Blocks are Objective-C objects, which means they can be added to collections like NSArray or NSDictionary .

What is ID in Obj C?

id is a data type of object identifiers in Objective-C, which can be use for an object of any type no matter what class does it have. id is the final super type of all objects.


1 Answers

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.

like image 114
Gabriele Petronella Avatar answered Sep 18 '22 09:09

Gabriele Petronella