I’m sure you’re all familiar with blocks, but do you really know how they work? Why __block, for example, and what is the use of the modifier? What are the consequences of not adding? How blocks are implemented and so on… This article will reveal the implementation principle of Block
The topic
So just to give you a question, let’s think about what happens if we call these two methods separately.
void blockFunc1()
{
int num = 100;
void (^block)() = ^{
NSLog(@"num equal %d", num);
};
num = 200;
block();
}Copy the code
void blockFunc2()
{
__block int num = 100;
void (^block)() = ^{
NSLog(@"num equal %d", num);
};
num = 200;
block();
}Copy the code
The answer is
blockFunc1 : num equal 100
blockFunc2 : num equal 200Copy the code
Did someone get that wrong? Two more functions. The result for both is the same as for blockFunc2, which prints num 200
Int num = 100; void blockFunc3() { void (^block)() = ^{ NSLog(@"num equal %d", num); }; num = 200; block(); }Copy the code
void blockFunc4()
{
static int num = 100;
void (^block)() = ^{
NSLog(@"num equal %d", num);
};
num = 200;
block();
}Copy the code
Question: If num is a local variable with the _ _block modifier, if num is a global variable, and if num is a static local variable with the _ _block modifier, the block output is the same. Num, which is a local variable and has no __block attached, outputs the same value in the block as before it was assigned. Why is that? To explore this question we need to look at how the underlying structure is implemented, right
Explore the inner workings
Objective-c is a fully dynamic language, and everything is implemented based on the Runtime! Create a Command Line Tool project and place the above code in main.m, as shown in the figure below
main.m
Here we will open the terminal, CD to the project directory, and rewrite OC to C with the following command
clang -rewrite-objc main.mCopy the code
rewrite-objc
At this point we can see that there is an extra main. CPP file in the current directory, open it and roll to the bottom
Open the main CPP
main.cpp
Here we can see the C implementation of blockFunc1
void blockFunc1()
{
int num = 100;
void (*block)() = ((void (*)())&__blockFunc1_block_impl_0((void *)__blockFunc1_block_func_0, &__blockFunc1_block_desc_0_DATA, num));
num = 200;
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}Copy the code
Get rid of the cast
void blockFunc1() { int num = 100; / / * * * * * * * * * * * * * * * * * * * * * * * * * the key words of * * * * * * * * * * * * * * * * * * * * * * * void () = (* block) &__blockFunc1_block_impl_0(__blockFunc1_block_func_0, &__blockFunc1_block_desc_0_DATA, num)); // ***************************************************** num = 200; ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); }Copy the code
And we can see that here
A block is actually a pointer to a structure
The structure is
__blockFunc1_block_impl_0
Let’s look at blockFunc2 with __block
blockFunc2
In blockFunc1, the block refers to a structure called blockFunc1_block_impl_0 and is initialized with three parameters (the last flags of blockFunc1_block_impl_0 have default parameters, so you don’t need to pass them). The third argument is the num, which, in contrast to blockFunc2, does not have an * sign, so it only passes values rather than addresses, whereas num = 200; There’s not much use for eggs. This is why blockFunc2, blockFunc3, and blockFunc4 print the changed value of num, but blockFunc1 does not.
Here we can also see:
The compiler generates the corresponding function from the block’s internal code
SO
In summary, a block is internally used as a pointer to a structure. When a block is called, it actually finds the corresponding function according to the corresponding pointer of the block, and then calls it and passes in itself
The realization of the __block
Let’s look at _block. _block is also converted to a structure with five variables
struct __Block_byref_num_0 { void *__isa; // isa pointer __Block_byref_num_0 *__forwarding; // The instance itself int __flags; int __size; int num; // our num value};Copy the code
The image corresponds to blockFunc2
__block int num = 100;Copy the code
When num is created and decorated with __block, these five variables are initialized when we execute
num = 200;Copy the code
Corresponds to the
(num.__forwarding->num) = 200;Copy the code
__forwarding is the instance itself, that is, the &num of __Block_byref_num_0. Find the num variable and change it from 100 to 200
This is the end of the disclosure of the internal implementation of Block. Hopefully this article will give you a deeper understanding of Block. Thank you for your patience!