A Block type

Block is of type Block_layout

The Block_layout structure is as follows:

struct Block_layout {

    void *isa;

    volatile int32_t flags; // contains ref count

    int32_t reserved;

    BlockInvokeFunction invoke;

    struct Block_descriptor_1 *descriptor; //

    // imported variables

};
Copy the code

The Block_descriptor structure is as follows:

#define BLOCK_DESCRIPTOR_1 1 struct Block_descriptor_1 { uintptr_t reserved; uintptr_t size; }; // Optional #define BLOCK_DESCRIPTOR_2 1 struct BLOCK_DESCRIPTOR_2 {// requires BLOCK_HAS_COPY_DISPOSE BlockCopyFunction copy; BlockDisposeFunction dispose; }; \ #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

Enumeration of flags in Block_layout:

Values for Block_layout->flags to describe block objects enum { BLOCK_DEALLOCATING = (0x0001), // runtime BLOCK_REFCOUNT_MASK = (0xfffe), // runtime BLOCK_NEEDS_FREE = (1 << 24), // runtime BLOCK_HAS_COPY_DISPOSE = (1 << 25), // compiler BLOCK_HAS_CTOR = (1 << 26), // compiler: helpers have C++ code BLOCK_IS_GC = (1 << 27), // runtime BLOCK_IS_GLOBAL = (1 << 28), // compiler BLOCK_USE_STRET = (1 << 29), // compiler: undefined if ! BLOCK_HAS_SIGNATURE BLOCK_HAS_SIGNATURE = (1 << 30), // compiler BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31) // compiler };Copy the code

Blocks are copied at compile time, whether you call them or not.

Three-layer copy of a Block:

Why blocks can capture variables:

1. When a block is created, the block itself is passed inside the block as a type parameter, but invisible at the OC level.

Copy order of blocks:

1. Block copies itself at the first layer by calling _Block_copy.

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 latching_incr_int(&aBlock->flags); return aBlock; } else if (aBlock->flags & BLOCK_IS_GLOBAL) { return aBlock; } else {// stack // Its a stack block. Make a copy. Struct Block_layout *result = (struct Block_layout *)malloc(aBlock->descriptor->size); if (! result) return NULL; memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first #if __has_feature(ptrauth_calls) // Resign the invoke pointer as it uses address authentication. result->invoke = aBlock->invoke; #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; return result; }}Copy the code

2. If a block captures an external variable, it copies the variable and stores the variable and block in the same memory. (Capture general variables, such as int, float)

// the __block variable void _Block_object_assign(void *destArg, const void *object, const int flags) { const void **dest = (const void **)destArg; switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) { case BLOCK_FIELD_IS_OBJECT: /******* id object = ... ; [^{ object; } copy]; ********/ // objc pointer address weakSelf (self) // arc _Block_retain_object(object); // hold *dest = object; break; case BLOCK_FIELD_IS_BLOCK: /******* void (^object)(void) = ... ; [^{ object; } copy]; ********/ // block is captured by a block *dest = _Block_copy(object); break; case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK: case BLOCK_FIELD_IS_BYREF: /******* // copy the onstack __block container to the heap // Note this __weak is old GC-weak/MRC-unretained. // ARC-style __weak is handled by the copy helper directly. __block ... x; __weak __block ... x; [^{ x; } copy]; ********/ *dest = _Block_byref_copy(object); break; case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT: case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK: /******* // copy the actual field held in the __block container // Note this is MRC unretained __block only. // ARC retained __block is handled by the copy helper directly. __block id object; __block void (^object)(void); [^{ object; } copy]; ********/ *dest = object; break; case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK: case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK | BLOCK_FIELD_IS_WEAK: /******* // copy the actual field held in the __block container // Note this __weak is old GC-weak/MRC-unretained. // ARC-style __weak is handled by the copy helper directly. __weak __block id object; __weak __block void (^object)(void); [^{ object; } copy]; ********/ *dest = object; break; default: break; }}Copy the code

3. Block captures an external variable modified by __block.

struct Block_byref_2 { // requires BLOCK_BYREF_HAS_COPY_DISPOSE BlockByrefKeepFunction byref_keep; / / object structure __block BlockByrefDestroyFunction byref_destroy; };Copy the code

__block modifiers have Block_byref_2

How to view the underlying principle of block:

1. The front-end of object-C compilation is LLVM, so you only need to use clang, a subset of LLVM, to perform the following operations:

Clang-rewrite-objc-fobjc-arc-fobjc-runtime =ios-13.0.0. Clang-rewrite-objc-fobjc-runtime =ios-13.0.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/ IPhoneSimulator. Platform/Developer/SDKs/iPhoneSimulator14.0. Main SDK. MCopy the code

2.Xcode encapsulates clang, so it is easier to compile using XCRun, regardless of the SDK version, so it is relatively easy to use. As follows:

'xcode' is installed with the 'xcrun' command, which encapsulates some of the 'clang', To better use some xcrun-sdk iphonesimulator clang-arch arm64-rewrite-objc viewController.m -o viewController-arm64.cpp Xcrun - SDK iphoneOS clang -arch arm64 -rewrite-objc ViewController. M -o ViewController-arm64. CPPCopy the code

The operation is as follows, CD to the file directory, the above command can be done, as shown in the picture below:

The rest is to look at the.cpp file for yourself. This command is invalid for swift, because swift is compiled in front of swift, and how swift is compiled will be updated later.