This is my 12th day of the August Challenge. In the last article we talked about the division of blocks and how to solve looping applications. In this article we will focus on the principle of block.
1. The Clang analysis
Define a c file about blocks:Compile using Clangclang -rewrite-objc block.c -o block.cpp
The equivalent of
void(*block)(void) = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA)); / / initialization
block->FuncPtr(block);// Call execution
Copy the code
Block is equivalent to __main_block_IMPL_0 being a function. Let’s look at what it’s made of
It’s a structure that containsimp
.desc
.ByRef
External variables.
__block_impl
Composition: __main_block_desc_0
Composition:
__Block
Externally decorated variables:__Block_byref_a_0
Structure:Callback function implementation:__main_block_func_0
Structure:
From Clang compilation we know: we use__block
The modified block is oneThe structure of the body
Object containing oneimpl
The implementation of our functionality; Description of a block and handling of external variables. Their relationship is as follows:
1.1 Invocation of block
((void(*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); Equals the following block->FuncPtr(block);// Call execution
Copy the code
When we Clang, we know that the block’s code block, {}, is the __block_impl structure, which contains the address of the FuncPtr implementation, which points to __main_block_func_0. We call access through a pointer implementation. Blocks are stored functionally and cannot be implemented if not called, and cannot be called back if not called in daily development.
Function declaration
The internal implementation of a block is declared as a function__main_block_func_0
Perform specific function implementations
: by calling a blockFuncPtr
Pointer to call block execution
1.2 Block captures external variables
// __block int a = 18;
int a = 18;
void(^block)(void) = ^ {// a++;
printf("a-count - %d",a);
};
block();
Copy the code
The compiled:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a){ impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};/ / implementation
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int a = __cself->a; __cself->a; // bound by copy
printf("a-count - %d",a);
}
Copy the code
Here: a(_a) is C++ syntax. By default, it will assign a value to the parameter a, _a will pass it to a, and the assignment will be done, and the underlying block will capture the variable as its own member variable. The ain __main_block_func_0 is a copy of the value, and if a++ is used in the internal implementation of a block, it is problematic and will cause confusion in the compiler code, which means that a is read-only: when a block captures an external variable, it automatically generates the same attribute internally to save it
1.3 __block modification
int main(){
__block int a = 18;
int a = 18;
void(^block)(void) = ^{
a++;
printf("a-count - %d",a);
};
block();
return 0;
}
/ / the compiled
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;//*__forwarding* this points to the address of A
int __flags;
int __size;
int a;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_a_0 *a; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding){ impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};/ / implementation
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_a_0 *a = __cself->a; // the __cself->a is the same as the __cself->a.
(a->__forwarding->a)++;
printf("a-count - %d",(a->__forwarding->a));
}
Copy the code
When initialized (__Block_byref_a_0 *)&a, take the address of a. __main_block_func_0 internal processing of a is a pointer copy, in which the created object A points to the same memory space as the passed object A. Conclusion: When we use __block to modify an external variable, we generate a __Block_byref_a_0 structure that holds the pointer and value of the original variable. Pass the pointer address of the structure object generated by the variable to the block, and then you can manipulate the external variable inside the block.
2. Block source code analysis
About compiled __main_block_copy_0, __main_block_dispose_0, __main_block_desc_0 these structures, they are used for what, let’s analyze the block copy process. Through breakpoints, we analyze the assembly process, debugging code is as follows
Breakpoint debugging starts for the stack block
callobjc_retainBlock
We giveobjc_retainBlock
Add sign breakpoint
加_Block_copy
Symbol breakpoint, run to break, atlibsystem_blocks.dylib
In the source
You can download the latest version from Apple’s open source websitelibclosure-79Source code, by viewing_Block_copy
Source code implementation,
2.1 Block_layout analysis
The true type of block found underneath isBlock_layout
- Contains a pointer to ISA, of type block
- Flags: The flags identifier is defined as
The specific explanation
-
Bit 1 – BLOCK_DEALLOCATING, freeing the mark, usually BLOCK_NEEDS_FREE does the bit and operation, passing it along with Flags to tell the block to be free.
-
Lower 16 bits – BLOCK_REFCOUNT_MASK, which stores the value of the reference count; Is an optional parameter
-
Bit 24 – BLOCK_NEEDS_FREE, which indicates whether the lower 16 is valid and determines whether to increase or decrease the value of the reference count bit;
-
Bit 25 – BLOCK_HAS_COPY_DISPOSE, whether it has a copy helper function;
-
Bit 26 – BLOCK_IS_GC, whether it has a block destructor;
-
27th, indicating whether there is garbage collection; //OS X
-
Bit 28 – BLOCK_IS_GLOBAL, indicating whether a global block is present;
-
Bit 30 – BLOCK_HAS_SIGNATURE, as opposed to BLOCK_USE_STRET, determines whether the current block has a signature. Used for dynamic invocation at Runtime.
- Invoke a function
- Descriptor other related descriptions, whether destructor is being performed, etc
Descriptor: Additional information about a block, such as the number of variables to keep, the size of the block, and Pointers to auxiliary functions to copy or dispose. Have three kinds of
Block_descriptor_1
Is choiceBlock_descriptor_2
和Block_descriptor_3
It’s all optional
2.2 Block memory changes
We break a global block
Read the register, at this timeblock
是Global block
, i.e.,__NSGlobalBlock__
type
Adding external variables
At this point the breakpoint of reading the block is still__NSStackBlock__
Continue with the process
- increase
_Block_copy
Sign breakpoint and break, add breakpoint directly in the last RET, read RAx, found through_Block_copy
And then, it becomes thetaHeap block
, i.e.,__NSMallocBlock__
Mainly becauseBlock address
Changed to a heap block
Conclusion: When a block contains external variables,compile
When isStack block
.The runtime
through_Block_copy
Copy the stack block toHeap area block
.
2.3 the signature
We know above that additional information about Block_layout descriptor_1 is required and Block_descriptor_2 and Block_descriptor_3 are optional.
The storage is contiguic in memory, and we also get Block_descriptor_2 elsewhere in the source code by translation
Flag is BLOCK_HAS_COPY_DISPOSEdesc2 stores copy information. If flag is BLOCK_HAS_SIGNATURE, desc3 stores signatures. We use LLDB to verify:
We’re reading the memory of the heap block, and we know the structure of the block before so,0x000000010bff3028
The corresponding isdescriptor
Desc1 is made up of resLoved and size. Desc1 is made up of resLoved and size. Desc1 is made up of resLoved and size0x000000010bff2de0And convertchar*
Print out the previous signature. Desc2 does not exist.
- Determine if there is
Block_descriptor_2
, namely the flagsBLOCK_HAS_COPY_DISPOSE
(copy auxiliary function) has a value
Block_layout->flags
Desc2 is only available for BLOCK_HAS_COPY_DISPOSE, so wep/x 1<<25
, that is, 1 is shifted 25 bits to the left, and its hexadecimal is 0x2000000
A value of 0 indicates that desc2 is not present.
- Verify Block_descriptor_3, flags is
BLOCK_HAS_SIGNATURE
The presence of DESC3 stores the signature
If yes, DESC3 exists.
- Verify signature:
[NSMethodSignature signatureWithObjCTypes:"v8@?0"]
, that is, print the signature
The block signature information is similar to the method signature information. It mainly shows the block return value, parameters, and types
2.3 _Block_copy analysis
void *_Block_copy(const void *arg) {
struct Block_layout *aBlock;
if(! arg)return NULL;
// The following would be better done as a switch statement
aBlock = (struct Block_layout *)arg;
if (aBlock->flags & BLOCK_NEEDS_FREE) {
// latches on high// release
latching_incr_int(&aBlock->flags);
return aBlock;
}
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
return aBlock;// Global block returns directly
}
else {// stack stack (compile time)
// Its a stack block. Make a copy.
size_t size = Block_size(aBlock);
struct Block_layout *result = (struct Block_layout *)malloc(size);// The heap area opens up space
if(! result)return NULL;
memmove(result, aBlock, size); // bitCopy first copies the stack to the heap
#if __has_feature(ptrauth_calls)
// Resign the invoke pointer as it uses address authentication.
result->invoke = aBlock->invoke;
#if __has_feature(ptrauth_signed_block_descriptors)
if (aBlock->flags & BLOCK_SMALL_DESCRIPTOR) {
uintptr_t oldDesc = ptrauth_blend_discriminator(
&aBlock->descriptor,
_Block_descriptor_ptrauth_discriminator);
uintptr_t newDesc = ptrauth_blend_discriminator(
&result->descriptor,
_Block_descriptor_ptrauth_discriminator);
result->descriptor =
ptrauth_auth_and_resign(aBlock->descriptor,
ptrauth_key_asda, oldDesc,
ptrauth_key_asda, newDesc);
}
#endif
#endif
// reset refcount
result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING); // XXX not needed
result->flags |= BLOCK_NEEDS_FREE | 2; // logical refcount 1
_Block_call_copy_helper(result, aBlock);
// Set isa last so memory analysis tools see a fully-initialized object.
result->isa = _NSConcreteMallocBlock;// Set ISA to heap block
returnresult; }}Copy the code
- Block_copy is mainly used to add
The stack block is copied to the heap block
.
1. Check whether the block is being released. If yes, release the block directly. 2. Check whether the block is a global block. 3. Calculate the size of block in stack area and create the same size of memory space in heap area. Copy the contents of block in stack area to heap area by memmove. 4. Set flags and set the block type _NSConcreteMallocBlock
2.4 _Block_object_assign analysis
blockCapturing external variables
_Block_object_assign will be called to perform a copy operation based on the modification type of the variable. The modification type is as follows:Continue to look at_Block_object_assign
The source code
When the operation of capturing external variables is mainly based on the type and modification of variables to do the corresponding operation:
BLOCK_FIELD_IS_OBJECT
: Reference count for ordinary variables+ 1
Operation.BLOCK_FIELD_IS_BLOCK
Void (^object)(void) = block_block_copy
Operation.
3. BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:__weak __block … x; The above modified variable X performs the _Block_byref_copy operation
_Block_byref_copy
Realize the source
- Process the captured object
Block_byref
Convert and save a copy. - If the external variable is not copied to the heap memory, allocate memory, proceed
copy
If yes, the system returns after processing. - Among them
copy
和src
theforwarding
The Pointers all point to the same piece of memory, that’s why, right__block
The modified object hasModify the
Reasons for ability
- Use __block to decorate variables
When compiled with Clang, the modified variables generate the Block_byref structureTo sum up: external variables__block
Decorated holding process:_Block_copy
Copy block from stack to heap ->_Block_byref_copy
Copy block object to Block_byref structure ->_Block_objct_asign
(Make a copy of the __block modified variable)
2.5 _Block_object_dispose analysis
Dispose is used to select the corresponding method through the modification of the block variable
It’s similar to copy
- To view
_Block_byref_release
3. Summary
1. A block is essentially a function, a structure, and an object. A block can be an anonymous function without a name. Block_layout contains ISA (block type, stack block or heap area), flags (some identification of block), reserved(reserved field),invoke (callback execution),desc(description information). If there are external variables in the block code block, the stack block is compiled, and the stack block is copied to the heap block by _Block_copy. 4. The block signature information is similar to the method signature information, mainly reflects the return value of the block. Parameters and types 5. When we use a __block modified external variable, we copy it into the _Block_byref structure using _Block_object_assign. 6. We release the external variables we hold when we destroy them.