The author of this article mainly refers to the book " Objective-C Advanced Programming: iOS and OS X Multithreading and Memory Management " written by Kazuki Sakamoto, Tomohiko Furumoto and translated by Li Hua. In my daily work, I encounter a lot of confusion related to Block, such as: Block's circular reference problem . So after reading the book repeatedly, I decided to summarize a related article about Block. If you still want to learn more about Block, the author recommends reading the above books directly. Due to the limited level of the author, there are bound to be flaws in the article, and I beg you readers to enlighten me. Block variable capture only targets variables used in Block. Unused variables will not be appended to the C language structure, which means they will not be captured by Block.
__xxx(外部函数名)_block_impl_x(序号,从0开始)
1. Capture of value variable types (such as int)
1. Global variables
- It will not be captured because it can be directly accessed in the C language function of Block transformation and can be used directly without any changes.
2. Global static variables
- It will not be captured because it can be directly accessed in the C language function of Block transformation and can be used directly without any changes.
3. Local static variables
- Capture , because the variable is outside the scope of the C language function of the Block transformation, a pointer will be generated pointing to the variable and saved in the C language structure of the Block transformation.
4. Ordinary local variables
- The capture mechanism that does not capture
local static variables seems to be applicable to the capture of local variables, but why hasn't the official done this?
Let me talk about my understanding here, 1) Because the original intention of local variables of ordinary value types is only to be used in the local scope, they do not want to be accessed by other places beyond the scope, so there is no need to capture them. 2) The purpose of capturing is that when the operation is performed inside the Block, the external variables can also be modified in the same way. For ordinary local variables of object type, such as an Array object, after adding an Object to it, obviously the external Array will also be added, because essentially they point to the same memory area. For ordinary local variables of value type, such as int type, if we want to modify it in the Block, the only way is to modify its value, but this is not allowed. Modifiers need to be added, but after adding
__block
the__block
modifiers , it becomes__block
a structure, no longer an ordinary value type.
2. Capture of pointer variable type (object) (such as: NSString)
Corresponding pointer variables (objects) are generated inside the Block structure , such as referencing external array objects, and id __strong array
member variables are generated inside the Block. The memory management of this member variable __xxx_block_copy_x
holds the external object by calling the function inside the Block. __xxx_block_dispose_x
to release the object.
__strong
Therefore, objects using local variables with modifiers ( default generated objects are of strong type ) in Block and variables copied to the heap __block
will exist beyond the scope of their variables because they are held by Block.
Note : If the external object is
__weak
modified by the modifier, the corresponding weak type object will be generated inside the Block structure. When the external object is released, because the weak pointer is automatically set to nil, the object inside the Block structure is set to nil. nil, although any method call can be made on it and it will not cause the program to crash, it will have no result. This is also often used to solve Block circular reference problems.
3. The use of __block for value variables (such as: __block int val = 0)
Generate __block
a structure. __block
A value type member variable is generated inside the structure, and its value is equal to the value of the external value variable. At this time, when modifications are made inside the Block, it will be done in the following form:
__Block_byref_val_0 *val = __cself->val;
(val->__forwarding->val) = 1;
Access itself through __block
the structure __forwarding
pointer (or a copy of itself on the heap) to change __block
the value inside the structure.
__block
__xxx_block_copy_x
The memory management of member variables holds the object by calling the function inside the Block and __xxx_block_dispose_x
releases the object.
4. The use of __block on pointer variables (objects) (such as: __block NSString *str)
__block
A structure will also be generated , and __block
an object type member variable will be generated inside the structure, which refers to an external object.
__block
__Block_byref_id_object_copy_x
The memory management of member variables holds the object by calling the function inside the Block and __Block_byref_id_object_dispose_x
releases the object.
Note : If the external object is
__weak
modifier and__block
modified,__block
the corresponding__weak
type of object will be generated inside the structure. When the external object is released, because the weak pointer is automatically set to nil,__block
the object inside the structure is set to nil. Although any method call can be made on it and it will not cause the program to crash, it will have no result.
5. __forwarding pointer of __block variable
__Block_byref_val_0 *val = __cself->val;
(val->__forwarding->val) = 1;
Inside the Block, you can obtain the held __block
variables and modify the values of the variables in the above way, but __forwarding
does the existence of pointers seem redundant?
__block
The pointer inside the variable __forwarding
allows __block
the variable to be accessed correctly regardless of whether the variable is configured on the stack or the heap __block
. Here’s why:
Configuration storage domain of __block variable | The impact when blocks are copied from the stack to the heap |
---|---|
the stack | Copied from stack to heap and held by Block |
heap | Held by Block |
Here is an analysis of a situation
__block int val = 0;
void (^blk)(void) = [ ^{
++val; } copy ];
++val;
blk();
NSLog(@"%d",val);
Print result: 2
At first, __block
the variables and Block are on the stack. When the Block calls copy
a method, the Block is copied to the heap, and the variables called internally __block
are also copied from the stack to the heap and are held by the Block. At this time, __block
the variables have variables on the stack. And variables on the heap, variables on the stack __block
will __forwarding
replace the value of the member variable pointer with __block
the address of the structure instance copied to the variable on the target heap.
There was a misunderstanding here, and I learned it again. At this time, variables on the stack/heap can be accessed at the same time __block
, but the same __block
variable is ultimately accessed. If no __block
copy of the variable from the stack to the heap occurs, then __block
the variables accessed inside/outside the Block belong to the same variable on the stack. __block
variable, and vice versa to access the same __block
variable on the heap
The specific reasons are as follows:
Configuration storage domain of __block variable | Access process (after copying __block variable) |
---|---|
the stack | val (stack) -> __forwarding pointer (pointing to val on the heap) -> num |
heap | val (heap) -> __forwarding pointer (pointing to itself on the heap) -> num |
Here is an original quote from the book: Through this function, whether you use
__block
variables in Block syntax, outside Block syntax, or__block
the variables are configured on the stack or heap, you can access the same__block
variable smoothly.
6. Timing of copying block from stack to heap
When ARC is enabled, in most cases the compiler will make appropriate judgments and automatically generate code to copy the Block from the stack to the heap. The following situations are applicable:
1. copy
When calling the instance method of Block
2. When Block is used as the return value of a function or when Block is passed in the parameter of a function
3. When assigning Block to __strong
a class with modifier id type or to a member variable of Block type
4. When passing a Block in usingBlock
a Cocoa framework method or Grand Central Dispatch
API contained in the method name
However, sometimes it is necessary to manually copy the Block from the stack to the heap. In this case, we use the " copy
instance method".