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 b
Again, by commandxcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
Then 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 one
int age
Member variables- The constructor takes an extra argument
int _age
- There is an extra argument at the end of the constructor
: age(_age)
This is the syntax of c++_age
Automatically assign values to member variablesage
(2) Then look at the block definition and assignment code in mainIn the use
block
Constructor generationblock
When using externally definedint a = 10
, because the parameters of c function are passed by value, so this is the external variablea
The value of the10
Passed on toblock
Constructor of__main_block_impl_0
, so a member variable inside a blockage
Will be assigned to10
.
(3) Look again at the functions encapsulated inside the blockYou can see what’s used in the print code
age
Is actually a member variable inside a blockage
Not the one we defined out thereage
Therefore, when a block is assigned, its member variablesage
Is assigned to the argument passed in by the constructor10
, so the final printed value is10
, regardless of the externalage
How to modify. Outside of theage
A member variable of a blockage
It’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 block
int *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 externalstatic
variableheight
The 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
*height
Accessed externallystatic
variableheight
The 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
,static
Student: 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
- 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.
- 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 theself
Will be theblockCapture?Compilation result displayblockrightself
The capture was made. But according to? We know that in the pictureblockLocated in thetest
Method, in fact any OC method, converted to the underlying C function, has two default arguments,self
和 _cmd
, so as the default argument to the functionself
Is 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 visitCLPerson
Member variable of_age
The time is throughself
+_age offset
To obtain_age
So in the OC code,_age
Be equal toself->_age
To be clear, we still need to use it hereself
, soblockStill need to be rightself
To 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