preface

This article will start from a few interview questions, from OC to C++ and then dive into the underlying source code, with you familiar with everything you need to know about block an iOSer.

  • Wechat: RyukieW
  • Wechat official account: LabLawliet
  • ๐Ÿ“ฆ Archive of technical articles
  • ๐Ÿ™ making
My personal project Minesweeper Elic Endless Ladder Dream of books
type The game financial
AppStore Elic Umemi

I. Classification of blocks

Global block, heap block, stack block.

1.1 NSGlobalBlockGlobal block

  • Located in global area
  • No variables are captured or only global static variables are captured

No variables are captured

<__NSGlobalBlock__: 0x100004030>
Copy the code

Only global static variables are captured

1.2 the heap block

  • Located in the heap area
  • inblockInternal useA local variableorOC properties, and assign toStrong referenceorCopy to modifyThe variables of

<__NSMallocBlock__: 0x1007049e0>
Copy the code

1.3 stack block

  • Located in the stack area
  • inblockInternal useA local variableorOC properties, butDon'tAssigned toStrong referenceorCopy to modifyThe variables of

<__NSStackBlock__: 0x7ffeefbff378>
Copy the code

1.4 Interview Questions (1)

What would the following output look like?

NSObject *obj = [NSObject new];
NSLog(@"%ld".CFGetRetainCount((__bridge CFTypeRef)(obj)));

void (^blockA)(void) = ^ {NSLog(@"%ld".CFGetRetainCount((__bridge CFTypeRef)(obj)));
};
blockA();

void (^ __weak blockB)(void) = ^ {NSLog(@"%ld".CFGetRetainCount((__bridge CFTypeRef)(obj)));
};
blockB();

void (^blockC)(void) = [blockB copy];
blockC();
Copy the code

1.5 Interview Questions (2)

What would the following output look like?

int num = 0;
void (^ __weak blockA)(void) = nil;
{
    void (^ __weak blockB)(void) = ^ {NSLog(@"%d", num);
    };
    blockA = blockB;
    NSLog(@ "=");
}
blockA();
Copy the code

1.6 Interview Questions (3)

What would the following output look like?

int num = 0;
void (^ __weak blockA)(void) = nil;
{
    void (^ blockB)(void) = ^ {NSLog(@"%d", num);
    };
    blockA = blockB;
    NSLog(@ "=");
}
blockA();
Copy the code

The heap/stack block lifecycle

Here combine two, three interview questions to explore.

2.1 Interpretation of interview Question 2

It’s quite possible that some students will give an answer that will only print =. This is a poor understanding of the life cycle of stack and heap blocks. The correct output is as follows

Why is that? The key is to misunderstand the lifecycle of blockB. The general understanding is as follows:

I’m not going to talk about why, but let’s look at interview question number three.

2.2 Interpretation of interview question 3

There has been a wild pointer access crash.

2.3 Life cycle of heap blocks

The breakpoint inside the braces shows that blockB is a heap block and has a value.

Assign to blockA.

BlockA is empty after the braces, indicating that blockB has been released.

As a heap block stored on the heap, its lifetime exists only inside the braces.

2.4 Life cycle of stack blocks

What about the life cycle of blockB in interview question 2?

BlockB is a stack block.

Assign to blockA.

BlockA’s address remains the same, indicating that blockB has not been released.

Because as a stack block stored on a stack it has the same lifetime as the call stack. Release when the function call stack ends.

Third, the underlying implementation of capturing variables

3.1 Capture of common variables

int num = 1;
void (^block)(void) = ^ {NSLog(@"%d", num);
};
block();
Copy the code

Let’s restore the above code to C++ :

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
Copy the code
  • blockThe essence of is a structure__main_block_impl_0If there are variables that need to be captured, the structure body generates a member to store, in this exampleint num;
  • __main_block_func_0Save theblockThe specific function that needs to be executed
  • __block_impl ๆ˜ฏ blockSpecific data, such as functions
  • __main_block_desc_0Is to describeblockCharacteristic of a data structure

So what’s going on in this code?

Create block;

1. Call the __main_block_IMPL_0 constructor

2. Pass in the address and variable num of __main_block_func_0 and __main_block_desc_0_DATA

Isa = &_nsConcretestackBlock; (Here the heap block corresponds to _NSConcreteStackBlock)

4, store flag bit: impl.Flags = Flags;

FuncPtr = fp;

6, description content assigned to: Desc = Desc;

Execute block

1, obtain __main_block_func_0 function: ((void (__block_impl *) (*)) ((__block_impl *) block) – > FuncPtr)

2, will block passed as a parameter to: ((void (__block_impl *) (*)) ((__block_impl *) block) – > FuncPtr) ((__block_impl *) block);

Function __main_block_func_0:

Int num = __cself->num;

2. Execute NSLog NSLog((NSString *)&__NSConstantStringImpl__var_folders_py_7v1wvf813z5bmw0hr97yplwc0000gn_T_main_e543c2_mi_1, num);

3.2 __block Capture of common variables

__block int num = 0;
void (^block)(void) = ^{
    num ++;
    NSLog(@"%d", num);
};
block(a);Copy the code

Convert the above code to C++

We found several additional data types: __Block_byref_num_0, __main_block_copy_0, and __main_block_dispose_0.

Add a __block variable, which is wrapped in a __Block_byref_num_0 data structure, and pass num as an address.

3.3 Capturing instance variables

Inactive instance variables are no different from 3.2.

NSNumber *num = @100;
void (^block)(void) = ^ {NSLog(@ "% @", num);
};
block();
Copy the code

3.4 global block

static const NSString *string = @"aaaaaaa";
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        void (^block)(void) = ^ {NSLog(@ "% @", string); }; block(); }}Copy the code

3.5 stack block

int num = 0;
void (^ __weak block)(void) = ^ {NSLog(@"%d", num);
};
block();
Copy the code

You may encounter a package error when restoring C++ here:

error: cannot create __weak reference because the current deployment target does not support weak references
        void (^ __attribute__((objc_ownership(weak))) block)(void)
Copy the code

Add some parameters:

Clang-rewrite-objc-fobjc-arc-fobjc-runtime =ios-13.0.0 main.m -o main.cppCopy the code

Here we find that __weak is actually wrapped in __attribute__((objc_ownership(weak))), but there’s nothing more useful here, so let’s go ahead and explore.

3.6 _NSConcreteStackBlock

Isa = &_nsConcretestackBlock; isa = &_nsConcretestackBlock; See the name is a stack block. That’s not what you see when you run the interrupt point. So let’s analyze what’s going on here.

4. Determination of block type at runtime

Since there are no concrete clues here, we set a breakpoint and open assembly. This is a global block.

4.1 Enter assembly breakpoint

4.2 objc_retainBlock

Find it in libobjc by libobjc.a.dylib ‘objc_retainBlock:

The relevant code was located in the OC source code, but the _Block_copy for the further call was not found, so we went back to assembly and continued with the next step.

4.3 _Block_copy

Libsystem_blocks = libsystem_block; libclosure = libsystem_block; libclosure = libsystem_block; libclosure = libsystem_block; libclosure = libsystem_block;

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) {
        /// According to the flag bit, need to release
        // latches on high
        latching_incr_int(&aBlock->flags);
        return aBlock;
    }
    else if (aBlock->flags & BLOCK_IS_GLOBAL) {
        // return a global block based on the flag bit
        return aBlock;
    }
    else {
        // copy a block to the heap
        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
        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.
        // Set ISA to heap block
        result->isa = _NSConcreteMallocBlock;
        returnresult; }}Copy the code

Assembler validates block type conversions

5.1 global block

All the way to _Block_copy, read register X0 (ARM64, x0 now stores the receiver of the message).

When you enter _Block_copy, you are already identified as a global block.

We see the output of such a data structure, Signature Invoke, and we’ll see what they represent later.

<__NSGlobalBlock__: 0x1006f8028> signature: "v8@? 0" invoke : 0x1006f5edc (/ private/var/containers/Bundle/Application / 49 da1cbf - A462 DE - 19721201954 - A - 4433-80 / BlockEe ยขA selections ยข. The app/BlockEe ยขA selections ยข` __41 - [ViewC ontroller touchesBegan:withEvent:]_block_invoke)Copy the code

5.2 the heap block

When you enter _Block_copy, the stack block is displayed.

<__NSStackBlock__: 0x16d6651b8> signature: "v8@? 0" invoke : 0x10279de90 (/ private/var/containers/Bundle/Application/A3A281E1 e28-8 f30-1993-4-3 eed05043a20 / BlockEe ยขA selections ยข. The app/BlockEe ยขA selections ยข` __41 - [ViewC ontroller touchesBegan:withEvent:]_block_invoke)Copy the code

As written in the source code, malloc is performed here:

Let’s look at x0 again before _Block_copy return:

This has become a heap block

<__NSMallocBlock__: 0x281030ab0> signature: "v8@? 0" invoke : 0x10279de90 (/ private/var/containers/Bundle/Application/A3A281E1 e28-8 f30-1993-4-3 eed05043a20 / BlockEe ยขA selections ยข. The app/BlockEe ยขA selections ยข` __41 - [ViewC ontroller touchesBegan:withEvent:]_block_invoke)Copy the code

_block captures changes to variables

When entering _Block_copy:

<__NSStackBlock__: 0x16b21d1a0> signature: "v8@? 0" invoke : 0x104be5da0 (/ private/var/containers/Bundle/Application / 76397764-704 - e - 4 f3 ca4-80 - D94500849B81 / BlockEe ยขA selections ยข. The app/BlockEe ยขA selections ยข` __41 - [ViewC ontroller touchesBegan:withEvent:]_block_invoke) copy : 0x104be5e04 (/ private/var/containers/Bundle/Application / 76397764-704 - e - 4 f3 ca4-80 - D94500849B81 / BlockEe ยขA selections ยข. The app/BlockEe ยขA selections ยข` __copy_help er_block_e8_32r) dispose : 0x104be5e3c (/ private/var/containers/Bundle/Application / 76397764-704 - e - 4 f3 ca4-80 - D94500849B81 / BlockEe ยขA selections ยข. The app/BlockEe ยขA selections ยข` __destroy_h elper_block_e8_32r)Copy the code

Before _Block_copy return

<__NSMallocBlock__: 0x2823a83f0> signature: "v8@? 0" invoke : 0x100825da0 (/ private/var/containers/Bundle/Application/D167E1D0-819-2772-42 c4-83 f1 fc7250d2d/BlockEe ยขA selections ยข. The app/BlockEe ยขA selections ยข` __41 - [ViewC ontroller touchesBegan:withEvent:]_block_invoke) copy : 0x100825e04 (/ private/var/containers/Bundle/Application/D167E1D0-819-2772-42 c4-83 f1 fc7250d2d/BlockEe ยขA selections ยข. The app/BlockEe ยขA selections ยข` __copy_help er_block_e8_32r) dispose : 0x100825e3c (/ private/var/containers/Bundle/Application/D167E1D0-819-2772-42 c4-83 f1 fc7250d2d/BlockEe ยขA selections ยข. The app/BlockEe ยขA selections ยข` __destroy_h elper_block_e8_32r)Copy the code

Here the data structure has changed again, more copy dispose.

5.3 stack block

By looking at the call stack, we see that objc_retainBlock is not called. The _Block_copy operation is not performed.

Six, Block_layout structure analysis

6.1 the isa

Block types: global, heap, and stack

6.2 flags

These flag bits indicate different use times, including runtime: Runtime; compile time: Compiler

// Values for Block_layout->flags to describe block objects
enum {
    /// is being released
    BLOCK_DEALLOCATING =      (0x0001),  // runtime
    /// reference count mask
    BLOCK_REFCOUNT_MASK =     (0xfffe),  // runtime
    /// need to release
    BLOCK_NEEDS_FREE =        (1 << 24), // runtime
    /// if you have copy helper function Block_descriptor_2 if you do
    BLOCK_HAS_COPY_DISPOSE =  (1 << 25), // compiler
    /// whether a block destructor is available
    BLOCK_HAS_CTOR =          (1 << 26), // compiler: helpers have C++ code
    /// flag whether there is a garbage collection MAC
    BLOCK_IS_GC =             (1 << 27), // runtime
    /// Whether it is a global block
    BLOCK_IS_GLOBAL =         (1 << 28), // compiler
    /// This corresponds to BLOCK_HAS_SIGNATURE
    BLOCK_USE_STRET =         (1 << 29), // compiler: undefined if ! BLOCK_HAS_SIGNATURE
    /// if there is a signature there is Block_descriptor_3
    BLOCK_HAS_SIGNATURE  =    (1 << 30), // compiler
    /// Have an extended Layout
    BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31)  // compiler
};
Copy the code

6.3 reserved

Keep field

6.4 invoke

A pointer to a function, which is exactly what needs to be done in a block.

6.5 descriptor

So here we’re looking at the Block_descriptor

This corresponds to the previous output:

<__NSMallocBlock__: 0x2823a83f0> signature: "v8@? 0" invoke : 0x100825da0 (/ private/var/containers/Bundle/Application/D167E1D0-819-2772-42 c4-83 f1 fc7250d2d/BlockEe ยขA selections ยข. The app/BlockEe ยขA selections ยข` __41 - [ViewC ontroller touchesBegan:withEvent:]_block_invoke) copy : 0x100825e04 (/ private/var/containers/Bundle/Application/D167E1D0-819-2772-42 c4-83 f1 fc7250d2d/BlockEe ยขA selections ยข. The app/BlockEe ยขA selections ยข` __copy_help er_block_e8_32r) dispose : 0x100825e3c (/ private/var/containers/Bundle/Application/D167E1D0-819-2772-42 c4-83 f1 fc7250d2d/BlockEe ยขA selections ยข. The app/BlockEe ยขA selections ยข` __destroy_h elper_block_e8_32r)Copy the code

Although the memory structure of Block_layout contains only Block_descriptor_1, it can be found by reading the source code that Block_descriptor_2 and Block_descriptor_3 are read by memory translation. What data is readable is determined by identifying bits.

Block_descriptor_1

Basic data, there must be

Block_descriptor_2

COPY related fields if BLOCK_HAS_COPY_DISPOSE flag is present.

Block_descriptor_3

Signature data, if there is BLOCK_HAS_SIGNATURE flag bit.

7. Block signature

We mentioned signatures in Block_descriptor_3 above. As in the above example output signature: “v8@? 0” looks familiar, but it’s actually Type Encodings, and we’ve seen that in properties.

  • v8@? 0
    • vIndicates that the return value is null
    • 8Represents the total size of the parameters
    • @?Said block,
    • 0Indicates the start of byte 0

Specific can compare official document

NSMethodSignature

We can also output detailed representation information via NSMethodSignature.

(lldb) po [NSMethodSignature signatureWithObjCTypes:"v8@?0"] <NSMethodSignature: 0xac5982543c221e27> number of arguments = 1 frame size = 224 is special struct return? NO return value: -------- -------- -------- -------- type encoding (v) 'v' flags {} modifiers {} frame {offset = 0, offset adjust = 0, size = 0, size adjust = 0} memory {offset = 0, size = 0} argument 0: -------- -------- -------- -------- type encoding (@) '@? 'Flags {isObject, isBlock} // is an object, Frame {} frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0} memory {offset = 0, size = 8}Copy the code

__block captures the life cycle of a variable

Let’s look at the principle of capturing variables again, which we continue to explore through assembly.

_Block_object_assign is called here

We found the relevant source code:

8.1 Capturing variable types

// Values for _Block_object_assign() and _Block_object_dispose() parameters
enum {
    // see function implementation for a more complete description of these fields and combinations
    / / / object
    BLOCK_FIELD_IS_OBJECT   =  3.// id, NSObject, __attribute__((NSObject)), block, ...
    /// block
    BLOCK_FIELD_IS_BLOCK    =  7.// a block variable
    /// __block modifies the variable
    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
    BLOCK_BYREF_CALLER      = 128.// called from __block (byref) copy/dispose support routines.
};
Copy the code

8.2 _Block_object_assign

//
// When Blocks or Block_byrefs hold objects then their copy routine helpers use this entry point
// to do the assignment.
//
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)) {
        // The object is captured
      case BLOCK_FIELD_IS_OBJECT:
        /******* id object = ... ; [^{ object; } copy]; * * * * * * * * /

        Static void (*_Block_retain_object)(const void * PTR) = _Block_retain_object_default; Static void _Block_retain_object_default(const void * unused) {} Static void _Block_retain_object_default(const void * unused) {
        _Block_retain_object(object);

        // Pointer copy, reference count +1
        *dest = object;
        break;

        // A block is captured
      case BLOCK_FIELD_IS_BLOCK:
        /******* void (^object)(void) = ... ; [^{ object; } copy]; * * * * * * * * /

        /** * _Block_copy */
        *dest = _Block_copy(object);
        break;
    
      case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
      case BLOCK_FIELD_IS_BYREF:
      // Capture a __block / __weak __block variable
        /******* // 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

8.3 _Block_byref_copy

__Block captures operational memory copies of external variables, etc

static struct Block_byref* _Block_byref_copy(const void *arg) {
    // Use Block_byref structure to convert
    struct Block_byref *src = (struct Block_byref *)arg;

    if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
        // If the object is managed by reference count, malloc to create a copy
        // 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;

        __block refers to the same memory space */ 
        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;
            }

            /** External variables are captured by calling byref_keep memory to save the life cycle of the captured variable */
            (*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

Here we come across the Block_byref structure used to wrap the captured variables, and a byref_keep life-cycle function call, but there are no clues as to their origin, so we continue to look for clues in C++.

8.4 Block_byref

In addition to Block_byref, Block_byref_2 and Block_byref_3 are also included. They, like the previous Block_descriptor_1, are also read and written to memory translation by flag bit

// __Block decorates a structure
struct Block_byref {
    void *isa;
    struct Block_byref *forwarding;
    volatile int32_t flags; // contains ref count
    uint32_t size;
};

// The __Block modifier byref_keep and byref_destroy functions - handle holding and destruction of objects inside
struct Block_byref_2 {
    // requires BLOCK_BYREF_HAS_COPY_DISPOSE
    BlockByrefKeepFunction byref_keep;
    BlockByrefDestroyFunction byref_destroy;
};

/ / layout
struct Block_byref_3 {
    // requires BLOCK_BYREF_LAYOUT_EXTENDED
    const char *layout;
};
Copy the code

8.5 understand the Block_byref structure using C++

To further understand the structure of Block_byref, let’s restore C++ to find clues:

__block NSNumber *num = @100;
void (^block)(void) = ^{
    num = @101;
    NSLog(@"% @", num);
};
block(a);Copy the code

/** The corresponding Block_byref type structure */
struct __Block_byref_num_0 {
  void *__isa;
__Block_byref_num_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 NSNumber *num;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_num_0 *num; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_num_0 *_num, int flags=0) : num(_num->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; .int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        // Block_byref constructor
        __attribute__((__blocks__(byref))) __Block_byref_num_0 num = {
            /** Block_byref void *isa; * /
            (void*)0./** struct Block_byref *forwarding; * /
            (__Block_byref_num_0 *)&num, 
            /** volatile int32_t flags for Block_byref; * / 
            33554432./** Block_byref uint32_t size; * /
            sizeof(__Block_byref_num_0), 
            /** Block_byref_2 BlockByrefKeepFunction byref_keep; * / __Block_byref_id_object_copy
            __Block_byref_id_object_copy_131, 
            / * * Block_byref_2 BlockByrefDestroyFunction byref_destroy; * /
            __Block_byref_id_object_dispose_131, 
            // *num
            ((NSNumber *(*)(Class, SEL, int(a))void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 100)};void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_num_0 *)&num, 570425344));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

    }
    return 0;
}
Copy the code

byref_keep

Call _Block_object_assign again (see 8.2 for details). This time, flag 131 is passed in, binary is 1000 0011, and BLOCK_FIELD_IS_OBJECT = 3, So the branch associated with BLOCK_FIELD_IS_OBJECT is executed for copy and reference count + 1.

static void __Block_byref_id_object_copy_131(void *dst, void *src) {
 _Block_object_assign((char*)dst + 40, * (void((* *)char*)src + 40), 131);
}
Copy the code

byref_destroy

Call _Block_object_dispose to dispose

static void __Block_byref_id_object_dispose_131(void *src) {
 _Block_object_dispose(*(void((* *)char*)src + 40), 131);
}
Copy the code

conclusion

__block captures variables for a triple copy:

  • Variable A ่ขซ __blockEmbellish, will beblockB ไปŽ The stack ไธŠ copy ๅˆฐ The heap ไธŠ
    • _Block_copy
  • blockBcaptureBlock_byref C ๅนถ copy
    • _Block_object_assign_Block_byref_copy
  • Block_byref CTo capture theVariable ATo copy
    • _Block_object_assignbyref_keep

The code word is not easy, welcome to like the collection follow ๐Ÿ‘‹

reference

  • libclosure