1. What is block
In everyday iOS development, we often see code like this:
These code blocks enclosed in braces for ‘^{}’ or ‘^(){}’ are called blocks.
2. The use of the block
2.1 Block Settings
There’s a global variable ‘aBlock’ set here, and it’s set in viewDidLoad
2.2 Invocation of block
The block is called when the touchesBegan is clicked on the screen
2.3 summarize
Blocks can be set in one place and called in another, making them very flexible.
3. The classification of the block
We print three blocks with the following code
- 1. Block1 does not capture external variables. It is of type __NSGlobalBlock__ and belongs to the global block.
- Block2 captures the external variable and strongly references it. The variable block2 is of type __NSMallocBlock__ and belongs to the heap block.
- 3. Block3 captures the external variable, but the variable block3 weakly references it. It is of type __NSStackBlock__ and belongs to the stack block.
4. Block’s circular reference solution
4.1 Why does circular Reference Occur
4.1.1 Normal Release
Normal release means that object A holds object B, and A calls the Dealloc method when releasing and sends A release signal to B. After B receives the signal, retainCount reference count -1; If B’s reference count is zero at this point, B also calls the Dealloc method to release.
4.1.2 Circular Reference
A circular reference means that object A holds object B, and B holds object A; So A’s reference count is always greater than zero, so A can’t call Dealloc and send RELEASE to B; Similarly, B’s reference count is always greater than 0, so it cannot call Dealloc and send release to A. Circular references cause two objects to hold each other previously and cannot be freed.
4.2 How can I Solve the Problem of Circular Reference
A circular reference to a block is held by an object and the block
Analysis:
- 1. Break self’s strong reference to block; This is not feasible, because if you break this relationship, there are no objects holding blocks, and blocks are destroyed as soon as they are created;
- 2. Break a block’s strong reference to self in this way.
4.2.1 Mode 1: Automatic Release
- 1. This is what we call a dance between strength and weakness
weak-strong-dance
;- 2. The __weak modifier weakSelf will be added to one
A weak reference table
Medium, weakSelf and self will pointSame memory address
It’s not going to cause selfReference count +1
, and weakSelf will release automatically;- 3.__strong Modified strongSelf is a temporary variable strongly referenced to weaSelf when the block is finished executing
Automatic release
If strongSelf is not declared here, weakSelf printed by NSLog will become nil when dealloc is called and dispatch_after is executed.
At this point, the holding relationship of self, block, weakSelf and strongSelf is shown as follows:
This breaks the block’s strong reference to self and relies on the mediator mode. When the block is done,strongSelf is automatically set to nil, freeing self. This mode is automatic release.
4.2.2 Mode 2: Manual Release
__block changes a temporary variable vc to hold self,block holds VC, and when the block is done, vc is released, breaking the block’s strong hold on self
4.2.3 Method 3: Pass self as an argument to the block
If you pass self as an argument to a block, the block will not hold self, and there will be no problem with circular references.
5. Underlying principles of Block
5.1 The nature of blocks
1. Implement the following blocks
2. Open the terminal, and run the CD command to go to the directory where the main.m file is storedclang
The commandxcrun -sdk iphonesimulator clang -arch x86_64 -rewrite-objc main.m
Compile the main.m file and getmain.cpp
file
3. Open the main. CPP file and find the implementation of the main function
You can see that block = &__main_block_IMPL_0, passing in two arguments (__main_block_func_0, &__main_block_desc_0_data), refers to a function pointer address
4. Check the implementation of __main_block_IMPL_0. The passed parameters __main_block_func_0 and __main_block_desc_0_DATA
- As can be seen from the picture above
__main_block_impl_0
It’s actually a structure that has two propertiesimpl
andDesc
, and a construction function of the same name__main_block_impl_0
;- 2. Parameter 1
__main_block_func_0
Is assigned to__main_block_impl_0 function
thefp
And is finally assigned toimpl.FuncPtr
;- Inpl. isa = &_nsConcretestackBlock;
- 4. The parameter 2
__main_block_desc_0_DATA
Is assigned toDesc
.So the essence of a block is an object, a structure, and a function. Since a block function has no name, it is also called an anonymous function and can also be called a closure.
5. What code does the block call compile into
FuncPtr(block), a function that passes a block when called, in the same way that all oc methods have ‘self’ and ‘sel ‘hidden arguments.
The code relationship for blocks before and after compilation is as follows
5.2 Why does block Need to be called
As we have seen from the previous block nature, when the block is created, the underlying structure is actually __main_block_IMPL_0, created by its constructor of the same name. The parameter 1, corresponding to the parameter fp, is assigned to impl->FuncPtr; So if the FuncPtr call is not executed, the block code will not be executed. The summary is as follows:
- 1. Function declaration: A block declares a function inside
__main_block_func_0
The code for this function is block{}
Code in;- 2. Function call: The function corresponding to the block is saved in the FuncPtr property, so you need to call the block with block->FuncPtr(block).
5.3 How does a block capture external variables
Compile the following code through clang into c++ code
There is a variable called a, so let’s see how variable A is captured by the block after compilation
- 1. You can see the value of variable A passing through the function
__main_block_impl_0
A constructor of the same name is passed in for the block;- In 2.
__main_block_impl_0
The structure defines oneint a;
To access the value of external variable A, so as to achieve the capture of external variables.
Conclusion:
Blocks capture external variables by defining the same type of namesake and variable in their corresponding structure.
5.4 __block principle
Implement the following code and execute it
The value of a variable modified by __block is changed outside the block after a++ inside the block.
Again, compile this code
- 1. Can be found, by
__block
The modified a becomes compiled__Block_byref_a_0
Structure of;- 2. A’s address is
__Block_byref_a_0
The type of__forwarding
Record;- 3. A value is
int a
To receive.
Now look at the constructor of a block__main_block_impl_0
An anonymous function corresponding to a block__main_block_func_0
- 1. The structure corresponding to block is defined
__Block_byref_a_0 *a
Property to receive external incoming_a
the__forwarding
Pointer. That is to say,Block of a
andExternal variable A
Point to the same memory address;- 1. 2. Smart refrigerator
__main_block_func_0
In the function(a->__forwarding->a) ++;
Is theThe address of the __block modifier variable A
The corresponding values are modified to operate on the same memory address. So when you add ++ to a inside the block, the outside of the block also receives a change in the value of A.
Conclusion:
- The __block principle is to compile a variable modified by __block at compile time
__Block_byref_ Variable name _0
Type of structure;- Passes inside the block
__Block_byref_ Variable name _0
The type of__forwarding
Pointer to external__Block_byref_ Variable name _0
Type variable;- Is passed when modifying a variable modified by __block
block->a->__forwarding->a
To modify a variable;- Changes to __block-decorated variables pass both inside and outside the block
__forwarding
Pointer finds the same memory address to modify.
5.5 The true type of the underlying block
Let’s explore the underlying type of a block using symbolic breakpoints and assembly to locate the source code
1. Break the point at the block and start assembly to run the code
2. Find through assembly analysisobjc_retainBlock
To press a symbolic breakpoint and run
3. Continue to open the _Block_copy symbol breakpoint and run the command
This leads us to the library libsystem_blocks. Dylib where the block source code resides.
Go to Apple’s open source site and download the latest libclosure-78 source code, open it and search for _Block_copy
As you can see from the _Block_copy implementation, the arg passed in is assigned to a block object of type Block_layout
Attributes in the _Block_copy structure:
1. Isa, which points to the class to which the block belongs;
Flag, which records some states of the block object, is an enumeration:
// Alone in the world // Values for Block_layout->flags to describe block objects enum {// Flags is passed to tell the block to release BLOCK_DEALLOCATING = (0x0001), and store the value of the reference count, Is an optional parameter BLOCK_REFCOUNT_MASK = (0xfffe), // runtime // if the lower 16 bits are valid, BLOCK_NEEDS_FREE = (1 << 24), // runtime // whether to have a copy assist function, (a copy helper function) decide block_description_2 BLOCK_HAS_COPY_DISPOSE = (1 << 25), Block C++ destructor BLOCK_HAS_CTOR = (1 << 26), // compiler: Helpers have C++ code helpers have C++ code OSX BLOCK_IS_GC = (1 << 27), // runtime // flag is global block BLOCK_IS_GLOBAL = (1 << 28), // Compiler // In contrast to BLOCK_HAS_SIGNATURE, check whether the current block has a signature, which is used to dynamically call BLOCK_USE_STRET = (1 << 29) at Runtime, // compiler: undefined if ! BLOCK_HAS_SIGNATURE = (1 << 30), // Compiler // Decide block_description_3 BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31) // Compiler};Copy the code
We focused on BLOCK_DEALLOCATING, BLOCK_HAS_COPY_DISPOSE, and BLOCK_HAS_SIGNATURE
3. Reserved: Reserved information. It can be understood that the reserved position is used to store information about internal variables of the block
4. Invoke: A function pointer to the block’s executing code
5. Descriptor: Reserved information of blocks, reserved bits, block size, copy and dispose auxiliary function pointer, which has three kinds:
The three descriptor values are as follows:
- 1. Descriptor1 must be used, return descriptor1;
- 2. Descriptor2 is through
flags&BLOCK_HAS_COPY_DISPOSE
Determining whether there is a value and obtaining it by memory translation;- 3. Descriptor3 is through
flags & BLOCK_HAS_SIGNATURE
Determining whether there is a value and obtaining it by memory translation;
Summary: The true type underlying a block is Block_layout.
5.6 Block memory changes
From the implementation of the _Block_copy function we can see:
- 1. Release if you need to release.
- 1. If a heap block is used, reture it directly;
- The else case is stack block, because the heap block needs to allocate memory manually; The process here is to request a block of memory and pass the stack block
memmove
Copy toBlock_layout
The type ofresult
Object and point isa to_NSConcreteMallocBlock
.
Through source code analysis, we clearly see how blocks from stack blocks to heap blocks.
5.7 Block Signature
Look at the memory layout of Block_layout, you can get the property descriptordescriptor of Block_layout by shifting memory by 3*8, mainly to check whether Block_descriptor_2 and Block_descriptor_3 are present. Where the property Descriptor3 has the block’s signature.
Signature indicates the signature of a block.
5.8 Layer-3 Copy of blocks
5.8.1 Layer 1 Copy: _Block_copy
Here we copy stack blocks to heap blocks
5.8.1 Layer 2 Copy: _Block_object_assign
First of all, we need to know what kinds of external variables are available. The most commonly used are BLOCK_FIELD_IS_OBJECT and BLOCK_FIELD_IS_BYREF
Enum {// implement for a more complete description of these fields and combinations // That is, no other reference types BLOCK_FIELD_IS_OBJECT = 3, // id, NSObject, __attribute__((NSObject)), block... BLOCK_FIELD_IS_BYREF = 8, BLOCK_FIELD_IS_BYREF = 8, // the on stack structure holding the __block variable BLOCK_FIELD_IS_WEAK = 16, // declared __weak, Only used in byref copy helpers // // called from __block (byref) copy/dispose support routines.};Copy the code
Analyze the source code for _Block_object_assign
- 1.
BLOCK_FIELD_IS_OBJECT
If it is a common object, it will be handed to the system arc processing, and copy the object pointer, that is, reference count +1, so the external variable can not be released;- 2.
BLOCK_FIELD_IS_BLOCK
: Passes if the variable is of block type_Block_copy
Operation,Blocks are copied from the stack to the heap
;- 3.
BLOCK_FIELD_IS_BYREF
Call if it is a __block decorated variable_Block_byref_copy
Function for memory copy and general processing;
The second copy that occurs here is in the _Block_byref_copy function, whose source code is analyzed below
Static struct Block_byref *_Block_byref_copy(const void *arg) {// struct Block_byref * SRC = (struct Block_byref * struct) Block_byref *)arg; if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) { // src points to stack struct Block_byref *copy = (struct Block_byref *)malloc(src->size); copy->isa = NULL; // byref value 4 is logical refcount of 2: one for caller, one for stack copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4; Copy ->forwarding = copy; // patch heap copy to point to itself src->forwarding = copy; // patch stack to point to heap copy copy->size = src->size; if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) { // Trust copy helper to copy everything of interest // If more than one field shows up in a byref block this is wrong XXX struct Block_byref_2 *src2 = (struct Block_byref_2 *)(src+1); struct Block_byref_2 *copy2 = (struct Block_byref_2 *)(copy+1); copy2->byref_keep = src2->byref_keep; copy2->byref_destroy = src2->byref_destroy; if (src->flags & BLOCK_BYREF_LAYOUT_EXTENDED) { struct Block_byref_3 *src3 = (struct Block_byref_3 *)(src2+1); struct Block_byref_3 *copy3 = (struct Block_byref_3*)(copy2+1); copy3->layout = src3->layout; } (*src2->byref_keep)(copy, src); } else { // Bitwise copy. // This copy includes Block_byref_3, if any. memmove(copy+1, src+1, src->size - sizeof(*src)); } } // already copied to heap else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) { latching_incr_int(&src->forwarding->flags); } return src->forwarding; }Copy the code
The Block_byref type is the compiled true type of the __block-modified variable. Copy the arG passed in from the outside into ‘copy’ and use the __forwarding pointer to point to the same memory address. Here is the copy of the external variable
5.8.3 Layer 3 Copy
Define a __block decorated NSString object
__block NSString *name = [NSString stringWithFormat:@"xjh"]; void (^block1)(void) = ^{ // block_copy lg_name = @"xjh"; NSLog(@"xjh - %@",lg_name); // block memory}; block1();Copy the code
Xcrun compiles as follows,
- There are more compiled names than normal variables
__Block_byref_id_object_copy_131
和__Block_byref_id_object_dispose_131
;__Block_byref_cjl_name_0
I have a lot of structures__Block_byref_id_object_copy
and__Block_byref_id_object_dispose
;
/ / * * * * * * * * the name of the compiled * * * * * * * * __Block_byref_name_0 name = {(void *) 0, (__Block_byref_name_0 *) & name, 33554432, sizeof(__Block_byref_name_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSString * _Nonnull (*)(id, SEL, NSString * _Nonnull, ...) )(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl__var_folders_hr_l_56yp8j4y11491njzqx6f880000gn_T_main_9f330d_mi_0)}; / / * * * * * * * * __Block_byref_name_0 structure * * * * * * * * struct __Block_byref_name_0 {void * __isa; __Block_byref_name_0 *__forwarding; int __flags; int __size; void (*__Block_byref_id_object_copy)(void*, void*); void (*__Block_byref_id_object_dispose)(void*); // 5*8 = 40 NSString *name; }; / / * * * * * * * * __Block_byref_id_object_copy_131 * * * * * * * * / / block their own copies (_Block_copy) - __block bref structure copy (_Block_object_assign) Static void __Block_byref_id_object_copy_131(void * DST, Void * SRC) {// DST external capture variable, that is, structure - 5*8 = 40, Name (name was assigned when bref was initialized) _Block_object_assign((char*) DST + 40, *(void * *) ((char*) SRC + 40), 131); } //********__Block_byref_id_object_dispose_131******** static void __Block_byref_id_object_dispose_131(void *src) { _Block_object_dispose(*(void * *) ((char*)src + 40), 131); }Copy the code
Use libclosure-74 to debug the compilable source code breakpoint. The key methods are executed in the following order: _Block_copy -> _Block_byref_copy -> _Block_object_assign
So how does a block get a name?
- 1. Run the _Block_copy method to copy a block to the heap.
- 2. Copy using the _Block_object_assign method because the __block variable is the underlying Block_byref structure;
- 3. Find that the external variable is a __block modified object, copy the corresponding object name from bref to the block space, then use it. * block_object_assign (*dest = object; * block_object_assign); See).
Conclusion:
- [Level 1] Through
_Block_copy
Implements a copy of the object itself fromThe stack area
Copy toThe heap area
;- [Layer 2] Through
_Block_byref_copy
Method to copy the compiled structure object of the true type of the external __block modified object asBlock_byref
Structure type;- [Level 3] call
_Block_object_assign
Method, a copy of the actual type variable modified by __block.
6. Summary
In daily development, we should first fully understand the block, proficient in the use of block, especially the solution of circular reference. Understanding the nature of blocks and the underlying principles helps us write high-quality code and use blocks flexibly.