This is the 30th day of my participation in the August Challenge

In the last blog post, I explored the essence of a block that the structure (__main_block_IMPL_0) inherits from __block_impl, that blocks can capture external variables, and that __block decorates the interior to change the value of external variables. This blog will examine the underlying principles for continuing to work with blocks.

IOS Basic Block Exploration (1) — Getting to know Blocks (How many blocks do you know?)

IOS Basic Exploration Block(2) — How to solve the Block circular reference problem?

IOS Basic Exploration of Blocks (iii) — The nature of blocks

1. Block tracing the source

In the past analysis is to find the source of the analysis object, and then look at the corresponding source code for analysis, so the block is from which library, it is not known, we are trying to find out.

Assembly view process

Through simpleblock Code to see the assembly call, whether there will be a different discovery!

As you can see from the process compiled above at 👆, one is calledobjc_retainBlock.objcThat’s what it starts withlibobjc.A.dylibSource code library! So I’m going to verify that again, using the sign breakpoint to see if that’s truelibobjc.A.dylib

Sign breakpoint validation

Through the symbol breakpoint, it is also verified that it is from the familiar libobjc.a.dylib source library, as shown below:

Running through the code again, I did go to the symbol breakpoint below, and also found that it was fromlibobjc.A.dylib, tested the above conjecture, andjmpJump to_Block_copy, the source code can also be verified:

It is known from the source that calling objc_retainBlock returns _Block_copy, but the implementation of the _Block_copy method is not found in the source code.

Since there is no source_Block_copyThe realization of a bold guess, is not inlibobjc.A.dylibThe inside? Then go down_Block_copySymbol breakpoint look not to know ah! As follows:

Through the_Block_copySymbol breakpoint trace, found_Block_copyComes from thelibsystem_blocks.dylibThis library, but this onelibsystem_blocks.dylibWithout open source, this wave of operations is annoying. So what to do? There are two ways that we already know fromlibsystem_blocks.dylibCan disassemble, there is a way is to findlibclosureInstead, it’s ok.

Search libclosure-79 for _Block_copy, which comes from the Block_layout structure in the block_private. h file.

Block_layoutIt’s in the structureisa, markflags,invokeThe function,descriptorDescription, etc.

inclangTo obtain thecppYou can see it in the fileblockSource code source, fromBlock_private.h, as shown in the figure below:

By comparison, it can also be found that the structure __block_impl defined by block in the CPP file is consistent with the structure of Block_layout in the source code, as shown in the following figure:

summary: through assembly debugging, under the symbol breakpoint, finally trace back to the sourceblockComes from thelibsystem_blocks.dylib, but it is not open source and can be used through thelibsystem_blocks.dylibDisassemble or passlibclosureTo do source analysis instead of source engineering.

2. Assemble to see block capture variable changes before and after

A block captures an external variable, which is a stack block at compile time, and is copied to the heap area at run time, becoming a heap block.

Before the change

Let’s analyze when this memory change occurs, as shown in the following figure:

Through assembly debugging, read register, found that when calling objc_retainBlock, read register X0 here is the simulator is rax, analyze the block data state is still in the stack area, so continue to go down the process, see what changes are made after calling _Block_copy.

After the change

Moving on, when _Block_copy is called, the following changes:

When _Block_copy is called, the change changes from NSStackBlock to NSMallocBlock, and the address changes and is copied from stack to heap.

This verifies that a block captures an external variable, is a stack block at compile time, and is copied to the heap by _Block_copy at run time, becoming a heap block.

_Block_copy source code analysis

The source code of the block has been located above, so take a look at the source code

  • Block_layout
truct Block_layout {
    void * __ptrauth_objc_isa_pointer isa;
    volatile int32_t flags; // contains ref count
    int32_t reserved;
    BlockInvokeFunction invoke;
    struct Block_descriptor_1 *descriptor;
    // imported variables
};
Copy the code
  • isa isaTo determine theblocktype
  • flagsIdentification code
  • reservedKeep field
  • invokeThe delta function, which is thetaFuncPtr
  • descriptorRelated Additional information

  • flags

  • _Block_copy source code analysis

  1. rightflagsWhich is reference counting to determine if it isBLOCK_NEEDS_FREEIt’s released. Go straight backaBlock
  2. Global or notblockIs also a direct returnaBlock
  3. If it’s not global then it’s a stackblockOr a pile ofblock, but this is compile time and cannot be the heap areablockIf memory is opened at compile time, the compiler is too stressed. So the compiler marks it as a stack block, when the compiler knows you’ve caught an external variable, toThe runtimeTo perform the associated memory clearing operation (malloc), in progressmemmoveCopy a
  4. For some other information, includinginvoke, signature (ptrauth_signed_block_descriptors) Information is also packaged inresult
  5. The lastisa = _NSConcreteMallocBlockReturns a heap of extentsblock

In the assembly view above, the before and after changes of captured variables are printed,lldbThe debugging information is displayedsignature,invoke,copy,disposeAnd so on. What are these?

NSMethodSignature signatureWithObjCTypes:”v8@?0″; NSMethodSignature signatureWithObjCTypes:”v8@?0″ The code?

This is Type Encodings, Type code. IOS provides a directive called @encode that can represent specific types as string encodings! This was introduced when we analyzed the structure of the class.

  • vsaidviod, no return value
  • 8Said the8bytes
  • @Said parametersid self
  • ?Unknown type (for function Pointers)
  • 0saididfrom0On the start

This information can also be seen in the following figure:

On the console, you can print Po [NSMethodSignature signatureWithObjCTypes:”v8@?0″] to view signature information.

Remember that extra information about flags and Descriptor up there

#define BLOCK_DESCRIPTOR_1 1
struct Block_descriptor_1 {
    uintptr_t reserved;
    uintptr_t size;
};
Copy the code

In Block_descriptor_1 reserved is reserved field, size is the size of the block.

If #define BLOCK_DESCRIPTOR_2 1, that is, BLOCK_DESCRIPTOR_2, then copy and dispose are printed at the console above.

#define BLOCK_DESCRIPTOR_2 1
struct Block_descriptor_2 {
    // requires BLOCK_HAS_COPY_DISPOSE
    BlockCopyFunction copy;
    BlockDisposeFunction dispose;
};
Copy the code

Block_descriptor_3 is optional. The flag field is used to determine whether the block contains the Block_descriptor_3 attribute

#define BLOCK_DESCRIPTOR_3 1
struct Block_descriptor_3 {
    // requires BLOCK_HAS_SIGNATURE
    const char *signature;
    const char *layout;     // contents depend on BLOCK_HAS_EXTENDED_LAYOUT
};
Copy the code

  • throughBlock_descriptorthegetThe method can be found,Block_descriptor_2Can be achieved byBlock_descriptor_1Address translation method to obtain
  • To obtainBlock_descriptor_3When the judgeBlock_descriptor_2If yes, you do not need to add itBlock_descriptor_2Address space of.

LLDB debugging verification, as follows

More content continues to be updated

🌹 if you like, give it a thumbs up 👍🌹

🌹 feel harvest, can come to a wave, collection + attention, comment + forward, so as not to find me next time 😁🌹

🌹 welcome everyone to leave a message exchange, criticism and correction, learn from each other 😁, improve themselves 🌹