Block portal 🦋🦋🦋
Exploring the nature of blocks (1) — Basic knowledge
Explore the essence of blocks (2) — the underlying structure
Explore the essence of blocks (3) — basic type of variable capture
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 previous section, we learned that a Block is also an OC object because it also has an ISA pointer in its underlying structure. For example, the following block:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
The definition of / / Block
void (^block)(void) = ^ () {NSLog(@"Hello World");
};
NSLog(@ "% @", [block class]);
NSLog(@ "% @", [block superclass]);
NSLog(@ "% @", [[block superclass] superclass]);
NSLog(@ "% @", [[[block superclass] superclass] superclass]);
}
return 0; } * * * * * * * * * * * * * * * * * * * * * * * operation result * * * * * * * * * * * * * * * * * * * * * * * * * *2019- 06- 05 14:44:53.179548+0800 Interview03-block[16670:1570945] __NSGlobalBlock__
2019- 06- 05 14:44:53.179745+0800 Interview03-block[16670:1570945] __NSGlobalBlock
2019- 06- 05 14:44:53.179757+0800 Interview03-block[16670:1570945] NSBlock
2019- 06- 05 14:44:53.179767+0800 Interview03-block[16670:1570945] NSObject
Program ended with exit code: 0
Copy the code
In the above code, we print the block type and the superclass type using the [XXX class] and [XXX supperClass] methods. You can see that the inheritance relationship looks like __NSGlobalBlock__->__NSGlobalBlock->NSBlock->NSObject which is also a good proof that a block is an object, because its base class is NSObject. And we know that the ISA member variable in the block must inherit from NSObject.
Its compiled form is as followsThe information in the figure indicates that the blockisa
The class pointing to is_NSConcreteStackBlock
. Shouldn’t the class isa points to be the same as the class printed when the program runs?
Here’s one more detail: Currently, LLVM compiler to generate the intermediate file is no longer the c + + form, and we are in the command line, is actually a through c + + files generated by the clang, both on grammatical details is distinct, but most of the logic and principle or similar, so by clang generated between c + + code, only for us as a reference, In the end, the result must be the result of the Runtime, because the Runtime will still process and adjust the intermediate code compiled before the program runs.
The type of the Block
There are three types of blocksHere we come to one by one analysis, first we review the memory layout of the program
- Code blocks take up very little space and are generally stored in a low address space in memory, where all the code we write is stored
- The data segment is used to store global variables
- The heap is dynamically allocated to hold objects generated by alloc in our code. Dynamically allocated memory requires the programmer to allocate and manage memory. For example, objects generated by ALLOc in OC need to be released by calling RELEas (MRC), and objects generated by MALloc in C must be released by free().
- The stack area system automatically allocates and destroys memory for local variables generated within the function
Let’s use a classic example to see where different types of blocks are stored!
(1) NSGlobalBlock(that is, _NSConcreteGlobalBlock)
If a block does not use/access an auto variable internally, it is of type __NSGlobalBlock__ and is stored in the data segment of the application
Let’s verify that with codeThe above three figures show that several other variables except the auto variable are accessed by the block, and the printed results are as follows
2019- 06- 05 16:38:31.885797+0800 Interview03-block[17590:1712446] __NSGlobalBlock__
Program ended with exit code: 0
Copy the code
The result shows that blocks are of type __NSGlobalBlock__. In fact, this type of block does not have many application scenarios, so it is rarely seen, just for understanding.
(2) NSStaticBlock(that is, _NSConcreteStaticBlock)
If a block uses/accesses an auto variable (the auto variable), it is of type __NSStaticBlock__ and stored in the application stack
As we continue to verify the wave, the previous code adjustments are as followsThe print result is as follows
2019- 06- 05 16:45:25.990687+0800 Interview03-block[17648:1721701] __NSMallocBlock__
Program ended with exit code: 0
Copy the code
Yi? Why is the result here__NSMallocBlock__
? Should not be__NSStaticBlock__
? The reason is that the current isARCEnvironment,ARCThe mechanics have already done some work for us, but to see what it is, let’s turn it offARCRunning through the code again, the output looks like this
2019- 06- 05 16:52:08.500787+0800 Interview03-block[17712:1730384] __NSStackBlock__
Program ended with exit code: 0
Copy the code
Well, we see that, without the help of ARC, the block type here is indeed __NSStackBlock__. In fact, we’re going to use this type of block in a lot of situations, because a lot of times, we’re going to use environment variables in blocks, and most of the environment variables are going to be auto variables, so think about it, if we don’t do anything, what’s going to happen? (💡 reminder: combined with the life cycle of stack area contents)
Let’s adjust the raw code as follows
#import <Foundation/Foundation.h>
void (^block)(void);// Global variable block
void test(){
int a = 10;
block = ^(){
NSLog(@" the value of a is --%d",a);
};
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
test();
block();
}
return 0;
}
Copy the code
Based on the above code, what would you expect to print? Would the value of A, 10, be printed correctly? Look at the results
2019- 06- 05 17:04:25.915160+0800 Interview03-block[17820:1746272The value of a is --- 272632584.
Program ended with exit code: 0
Copy the code
Look, the value of A is 272632584, and obviously, if we use this value in our program, we will definitely break our original design idea.
So let’s analyze it:
- The code,
block
Is a global variable defined outside of a function - In the function
test()
Within the code^(){NSLog(@"a = --%d",a); };
It’s going to generate one for us first__NSStaticBlock__
The type ofBlock, it stores with the current functiontest()
And then its pointer is assigned to the global variableblock
. - in
main
Function, the function is called firsttest()
, global variablesblock
Just point to thetest()
This one on the stack__NSStaticBlock__
The type ofBlockAnd thentest()
The call ends and the stack space is reclaimed - then
block
That’s where the problem is, at this point,test()
The stack space is being reclaimed by the system to do other things, that is, the top one__NSStaticBlock__
The type ofBlockMemory is also reclaimed. Although throughObject block,
(or,Pointer to the block
), and finally access the original variablea
Is pointing to the block of memory, but the value of this inch is not guaranteed to be what we need10
So you can see that the print result is an unexpected number.
❓❓ So how to solve this problem? It’s natural to think that we need that
__NSStaticBlock__
The type ofBlockIt is moved to the heap so that it is not destroyed when the function stack area is reclaimed, but can be destroyed after the programmer has finished using it.
(3) NSMallocBlock(that is, _NSConcreteMallocBlock)
__NSMallocBlock__ is converted to __NSMallocBlock__ by calling the copy method, which is stored on the heap
Adjust the above code as follows
#import <Foundation/Foundation.h>
void (^block)(void);// Global variable block
void test(){
int a = 10;
block = [^(){ NSLog(@" the value of a is --%d",a); } copy];
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
test();
block();
NSLog(@" Block is of type %@",[block class]);
}
return 0;
}
Copy the code
Before assigning a block value, copy is performed. The following output is displayed
2019- 06- 05 17:44:16.940492+0800 Interview03-block[18166:1799723The value of a is --- 10
2019- 06- 05 17:44:16.940752+0800 Interview03-block[18166:1799723] Block is of type __NSMallocBlock__ Program ended withexit code: 0
Copy the code
As you can see, the print value of variable A is still 10, and the block does point to an __NSMallocBlock__. [^(){NSLog(@”a = –%d”,a);} copy]; The returned Block is stored on the heap, so the value of a in it is the same as when it was captured, 10, so the print is not affected.
You might be wondering, what if you call the copy method on __NSGlobalBlock__? The result is still an __NSGlobalBlock__. If you are interested, you can code it yourself.
conclusion
Calling copy on each type of block results in the following
Block copy problem in ARC environment
In the above section, we discussed the storage of blocks in memory based on the MRC environment. Since the blocks we generate in normal code are created within a function, that is, they are of type __NSStaticBlock__, and we usually need to save them and call them at some point in the future, but at that point the function stack on which the block is located no longer exists, so under MRC, We need to copy the contents of __NSStaticBlock__ to the heap memory by calling copy to make it a __NSMallocBlock__, so that it doesn’t affect future use, and as a consumer, we need to make sure that after using the block, we don’t need it again. Call the release method on the block to release it to avoid memory leaks.
ARC has done a lot of tedious and meticulous work for us developers, so that we do not have to spend too much energy on memory management, including block copy processing. For example, let’s tweak the previous code to remove the copy operation, as follows
#import <Foundation/Foundation.h>
void (^block)(void);// Global variable block
void test(){
int a = 10;
block = ^(){ NSLog(@" the value of a is --%d",a); };
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
test();
block();
NSLog(@" Block is of type %@",[block class]);
}
return 0;
}
Copy the code
Turn the ARC switch on and run the program and we get the following result
2019- 06- 05 20:29:31.503282+0800 Interview03-block[19472:1922021] * * * * * * * * * * * *10
2019- 06- 05 20:29:31.503652+0800 Interview03-block[19472:1922021] Block is of type __NSMallocBlock__ Program ended withexit code: 0
Copy the code
As you can see, this is the same as if we had copied the block manually under MRC, indicating that ARC did the copy for us.
In an ARC environment, the compiler automatically copies blocks on the stack to the heap as necessary, as in the following case
- Block is returned as a function argument
- Assign a block to
__strong
Pointer time- Block asCocoa APIThe Chinese name contains
usingBlock
Method parameter- Block as a method parameter of the GCD API
Small details – how Block attributes are written
- Suggestions for writing Block attributes under MRC
@property (nonatomic, copy) void(^block)(void);
- ARC Block attribute writing suggestions
@property (nonatomic, copy) void(^block)(void); // recommend @property (nonatomic, strong) void(^block)(void);
The ARC keyword copy and strong have the same effect on the block attribute, because ARC automatically copies the block when the __strong pointer points to it, but it is recommended to use the copy keyword for code consistency.
Block portal 🦋🦋🦋
Exploring the nature of blocks (1) — Basic knowledge
Explore the essence of blocks (2) — the underlying structure
Explore the essence of blocks (3) — basic type of variable capture
Explore the nature of blocks (5) — variable capture of object types
Exploring the nature of blocks (6) — An in-depth analysis of __blocks