Do you really understand the principle of the __block modifier?

Source: Internet
Author: User
Tags ocaml

Self test

At the beginning of this article, ask two simple questions, if you can not understand the root of these two problems, then I hope you read the article after the harvest.

    1. Why can't I change the value of a normal variable in a block?
    2. __block's role is to let the value of the variable be modified in block?

If some readers think that the problem is too simple, and your answer is:

    1. Because the compiler will have warnings, the various tutorials have also said that they cannot be modified.
    2. Yes, it should be.

Then I suggest you take a few minutes of your time to read through this article. Before we begin to uncover the mysteries of __block, it is unfortunate that we need to rethink the nature of the block and its implementation.

What is block?

Many tutorials and materials say block is "anonymous function with automatic variable value". This explanation is obviously correct, but it is also bad for beginners to understand. Let's start with an example to see what block is.

typedefvoid (^Block)(void);Block block;{    int0;    block = ^(){        NSLog(@"val = %d",val);    };}block();

Aside from blocks, there's a little weird syntax to talk about, actually for a block:

It's more like a mini-program.

Why do you say that, we know that the program is the data plus the algorithm, obviously, block has its own data and algorithms. As you can see, in this simple example, the block's data is the INT type variable val, and its algorithm is a simple NSLog method. For a typical block, its data is the parameters passed in and the variables intercepted when the block is defined. And its algorithm is that we write to the inside of those methods, function calls and so on.

One of the main reasons I think the block is like a mini-program is that a block object can be chosen by the programmer when it is called. For example, if I like, I can set a timer, execute the block after 10s, or execute the block in another class.

Of course, we also notice that in the demo above, the Typedef,block is very similar to an OC object. Confined to space and themes, here is an inconclusive conclusion: block is actually a Objective-c object. Interested readers can combine the definition of classes and objects in the runtime for further thought.

How is block implemented?

We have just realized that the definition and invocation of block are separate. With the clang compiler, you can see that blocks, like other objective-c objects, are compiled into a common struct structure in C. Let's see what the simplest block will be compiled into:

//这个是源代码int main(){    void (^blk)(void) = ^{printf("Block\n");};    block();    return0;}

The compiled code is as follows:

struct__block_impl {void*isa;intFlags;intReserved;void*funcptr;};struct__main_block_impl_0 {struct__block_impl Impl;struct__main_block_desc_0 *desc; __main_block_impl_0 (void*FP,struct__main_block_desc_0 *desc,intflags=0) {Impl.isa = &_NSConcreteStackBlock; Impl.    Flags = flags; Impl.    funcptr = FP; desc = desc;}};struct void__main_block_func_0 (struct__main_block_impl_0 *__cself) {printf("block\n");}Static struct__main_block_desc_0{unsigned LongReservedunsigned LongBlock_size;} __main_block_desc_0_data = {0,sizeof(struct__MAIN_BLOCK_IMPL_0)};

The code is very long, but it's not complicated, it's a total of four structs, and obviously a block object is compiled for a __main_block_impl_0 type of struct. The structure consists of two member structures and a constructor. The two structures are __block_impl and __main_block_desc_0 types respectively. Where there is a function pointer in the __BLOCK_IMPL struct, the pointer will point to the struct of the __main_block_func_0 type. Summarizes a pair of diagrams:

Block at the time of definition:

//调用__main_block_impl_0结构体的构造函数struct __main_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);struct __main_block_impl_0 *blk = &tmp;

When the block is called:

(*blk->impl.FuncPtr)(blk);

As we said before, block has its own data and algorithms. Obviously the algorithm (i.e. code) is placed in the __MAIN_BLOCK_FUNC_0 structure. So where is the data, the problem is more complicated, let's take a look at how the original demo will compile, to simplify the code, just put the parts that need to be modified.

struct__main_block_impl_0 {struct__block_impl Impl;struct__main_block_desc_0 *desc;int Val; __main_block_impl_0 (void *FP,struct__main_block_desc_0 *desc,int_val,intflags=0) :Val(_val)    {Impl.isa = &_NSConcreteStackBlock; Impl.    Flags = flags; Impl.    funcptr = FP; desc = desc;}};structvoid __main_block_func_0 (struct__main_block_impl_0 *__cself) {int Val= __cself->Val; printf"val =%d",Val);}

As you can see, when a block needs to intercept an automatic variable, it first adds a member variable to the __MAIN_BLOCK_IMPL_0 struct and assigns a value to the variable in the struct's constructor. These correspond to the definition of the block object.

When the block is executed, the __main_block_impl_0 struct, which is the block object, is passed into the __main_block_func_0 struct as a parameter, and the Val value is taken out for the next operation.

Why can't I change the value of a variable in __block?

If you look patiently at the very tedious block introduction above, you will soon see why the values of ordinary variables cannot be modified in the block.

By splitting blocks into these four structures, the system "perfectly" implements a block that allows it to intercept automatic variables and can be called at any moment, just like a tiny program. However, the block still has this fatal deficiency:

Notice that the previous __MAIN_BLOCK_FUNC_0 structure, which has the printf method, uses the variable Val, but the block, and the block intercepted by the original block, have no place in the same way as the values. See this code:

intval = __cself->val;

This, of course, has no effect, and even benefits, because the INT val variable is defined on the stack and is actually destroyed when the block is called, but we can also access the variable normally. But imagine that if I wanted to change the value of a variable in a block, then it would be an int val instead of a __cself->val, and in fact, even __cself->val, a copy of the automatic variable that was intercepted, It is impossible to modify an automatic variable outside the block definition. That's why I slightly modified the demo to add a line of code, but the output is still "val = 0".

//修改后的demovoid (^Block)(void);Block block;{    val0;    block = ^(){        NSLog(@"val = %d",val);    };    val1;}block();

Since it is not possible to modify automatic variables intercepted, the compiler simply prohibits the programmer from doing so.

How the __block modifier modifies the value of a variable

What will the compiler do if you add the Val variable to the __block modifier?

    //int val = 0; 原代码    int0;//修改后的代码

Post-compiled code:

struct__block_byref_val_0 {void *__isa; __block_byref_val_0 *forwarding;int__flags;int__size;int Val;};struct__main_block_impl_0 {struct__block_impl Impl;struct__main_block_desc_0 *desc; __BLOCK_BYREF_VAL_0 *Val; __main_block_impl_0 (void *FP,struct__main_block_desc_0 *desc,__block_byref_val_0 *_val,intflags=0) :Val(_val->__forwrding)    {Impl.isa = &_NSConcreteStackBlock; Impl.    Flags = flags; Impl.    funcptr = FP; desc = desc;}};structvoid __main_block_func_0 (struct__main_block_impl_0 *__cself) {__BLOCK_BYREF_VAL_0 *Val= __cself->Val; printf"val =%d",Val->__forwarding->Val);}

The change is not big, simply put, just a Val encapsulated in a struct. The following diagram can be used to represent the relationship between the five structure bodies.

But the key is to __main_block_impl_0 this line in the struct:

__Block_byref_val_0 *val;

Since a pointer variable is now stored in the __main_block_impl_0 struct, any manipulation of the pointer can affect the original variable.

Further, we consider the case of intercepting an automatic variable that is a Objective-c object. In the case of arc opening, this object will be strongly referenced once. This also ensures that the original object is not destroyed, but at the same time, it can cause circular reference problems.

It is important to note that, in the case where arc is not turned on, if the variable is accompanied by the __block modifier, it will not be retain, so you can avoid the problem of circular referencing.

Summarize

Back to the beginning of the two questions, the answer should be obvious.

    1. Because the original variable cannot be obtained directly, the modification is not technically possible, so the compiler is directly forbidden.
    2. Can be used to allow variables to be modified in the block, but in non-arc mode, the __block modifier avoids circular references. Note: The block's circular reference is not caused by the __block modifier, but by its own characteristics.

Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.

Do you really understand the principle of the __block modifier?

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.