Block portal 🦋🦋🦋

Exploring the nature of blocks (1) — Basic knowledge

Explore the essence of blocks (2) — the underlying structure

Explore the nature of blocks (4) — the type of Block

Explore the nature of blocks (5) — variable capture of object types

Exploring the nature of blocks (6) — An in-depth analysis of __blocks

In the last article, we looked at the underlying structure of the embryonic Block. Now we’re going to add some ingredients

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        The definition of / / Block
        void (^block)(int.int) = ^ (int a, int b){
            NSLog(@"I am a block! - %d - %d", a, b);
        };
        / / Block calls
        block(10.20);
    }
    return 0; } * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * log output * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *2019- 06- 04 15:30:57.747093+0800 Interview03-block[3915:354992] I am a block! - 10 - 20
Program ended with exit code: 0
Copy the code

Here we add two arguments to the function that block encapsulatesA and bAgain, by commandxcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cppThen open the compiled c++ fileObviously, parameters A and B are also encapsulated in the block, which is also relatively easy to pass.

Block captures the auto variable

Now let’s look at this situation

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int age = 10;
        The definition of / / Block
        void (^block)(void) = ^ () {NSLog(@"Age is %d", age);
        };
        // Change the age value first
        age = 20;
        / / Block calls
        block();
    }
    return 0;
}
Copy the code

I defined an int a = 10 before the block, and then I used the age inside the block, and I changed the age to 20 before calling the block, so what happens when the program runs? I believe that most of you know block well enough to give the correct answer.

2019- 06- 04 15:46:01.244557+0800 Interview03-block[4064:375528] Age is 10
Program ended with exit code: 0
Copy the code

The result is that the a printed in the block is 10. The changes we made to the age outside the block have no effect on the printing inside the block. Why? Let’s take a look at it again with the help of a compiled c++ file.

(1) First look at the structure corresponding to the block at this time We found three changes

  • There’s a new oneint ageMember variables
  • The constructor takes an extra argumentint _age
  • There is an extra argument at the end of the constructor: age(_age)This is the syntax of c++_ageAutomatically assign values to member variablesage

(2) Then look at the block definition and assignment code in mainIn the useblockConstructor generationblockWhen using externally definedint a = 10, because the parameters of c function are passed by value, so this is the external variableaThe value of the10Passed on toblockConstructor of__main_block_impl_0, so a member variable inside a blockageWill be assigned to10.

(3) Look again at the functions encapsulated inside the blockYou can see what’s used in the print codeageIs actually a member variable inside a blockageNot the one we defined out thereageTherefore, when a block is assigned, its member variablesageIs assigned to the argument passed in by the constructor10, so the final printed value is10, regardless of the externalageHow to modify. Outside of theageA member variable of a blockageIt’s two different variables that don’t affect each other.

The age variable outside the block is a local auto variable. The age variable is a local auto variable. We know that in addition to auto, there are local static variables and global variables in C. Let’s see how Block handles these variables.

Block captures local static variables

First we will transform the above OC code as follows

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int age = 10;
        static int height = 10;
        The definition of / / Block
        void (^block)(void) = ^ () {NSLog(@"Age is %d, height is %d", age, height);
        };
        // Change the age and height values first
        age = 20;
        height = 20;
        / / Block calls
        block();
    }
    return 0;
}
Copy the code

We added a static variable height and changed it in the same place to make it easier to compare with age. First run the code to see the results

2019- 06- 04 17:10:12.935220+0800 Interview03-block[4725:476530] Age is 10, height is 20
Program ended with exit code: 0
Copy the code

As you can see, the height of the block output is the 20 we reassigned externally. Why, let’s go into the compiled C++ file and see what it is

(1) Just like the above analysis process, first take a look at the structure corresponding to the blockYou see, the static variable height is added to the blockint *height;Member variables, and the corresponding arguments in the constructor areint *_height. And if you look at this, it should be pretty obvious that what we’re going to store here is an address, and the change address is going to be externalstaticvariableheightThe address value of.

(2) Let’s look at block assignment in mainIt’s pretty clear that what’s passed in the block constructor is the external address of height.

(3) Finally look at the functions inside the blockAs you can see, the function inside the block also uses the address value stored in the block*heightAccessed externallystaticvariableheightThe value of the.

Therefore, when we change height from the outside, the printed value of the block changes accordingly, because the inside of the block refers to the static variable height from the outside through a pointer.

❓ think about ❓ forauto,staticStudent: Variable, whyblockChoose to deal with them differently?

As we know, the storage space of an auto variable is located in the stack space of the function, it is created when the function opens the stack space, and destroyed when the function ends. However, the call time of the block may occur after the function ends, so the automatic variable cannot be used. Therefore, in the process of defining the assignment at the beginning of the block, It copies the value of the automatic variable to its own storage space.

Static changes the life cycle of a local variable so that it lasts for the entire duration of the program. So a block chooses to hold a pointer to a local variable that it can access when called.

Blocks use global variables

We talked about block handling of local variables, but let’s see what happens to global variablesThe output is as follows

2019- 06- 05 09:19:08.854599+0800 Interview03-block[13997:1263406] Age is 20, height is 20
Program ended with exit code: 0
Copy the code

Generate the compiled C++ file from the command line, again at the bottom of the file to seeThis time it was very pleasant,blockThere is no global variable capture behavior, just need to use the name of the variable, because the global variable is cross-function, can be directly accessed by the name of the variable. Also, it helps me understand why for local variables,blockLocal variables need to be “captured” because they are inside the function and cannot be used across functions, so local variables can be stored according to their properties or their values can be copied directly (auto), or make a copy of its address (static).

conclusion

  1. Local variables are captured by blocks
  • Automatic variable (auto),blockCapture by value copy, create a variable of the same type inside it, and copy the value of the automatic variable toblockInternal variables of,blockWhen a block of code executes, it accesses this one directlyInternal variables.
  • Static variable (static),blockIn the address copy mode, a pointer to a variable of the same type is created inside it, and the address value of the static variable is copied toblockThis pointer inside,blockWhen a block of code executes, the pointer is stored internallyIndirectly access static variables.
  1. Global variables are not captured by blocksBlock code blocks are executed by global variable namesDirect access to the.

Block to self

Above, pleaseblockThe inside of theselfWill be theblockCapture?Compilation result displayblockrightselfThe capture was made. But according to? We know that in the pictureblockLocated in thetestMethod, in fact any OC method, converted to the underlying C function, has two default arguments,self_cmd, so as the default argument to the functionselfIs actually a local variable of the function, and according to the principle we summarized above, as long as it is a local variable,blockThey capture it, and that explains it.

So one of the questions one might ask (especially the interviewer) isFirst look at the compilation resultYou can see that there’s still a capture going on, and if you look at the yellow box that I’ve marked in the picture,blockThe final visitCLPersonMember variable of_ageThe time is throughself +_age offsetTo obtain_ageSo in the OC code,_ageBe equal toself->_ageTo be clear, we still need to use it hereself, soblockStill need to be rightselfTo capture.

At this point, we’re done sorting out how blocks handle and call environment variables of the underlying type.

Block portal 🦋🦋🦋

Exploring the nature of blocks (1) — Basic knowledge

Explore the essence of blocks (2) — the underlying structure

Explore the nature of blocks (4) — the type of Block

Explore the nature of blocks (5) — variable capture of object types

Exploring the nature of blocks (6) — An in-depth analysis of __blocks