美文网首页
block 的底层逻辑,变量的捕获, 以及__block 的底层

block 的底层逻辑,变量的捕获, 以及__block 的底层

作者: 答案不止一个 | 来源:发表于2020-11-12 21:52 被阅读0次

block 经常被用到,那么再底层它是如何被实现的,以及 相关的变量捕获和__block 是怎么处理的呢

准备工作
  1. clang 命令,将.m 转称.cpp
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc xxx.m
  1. libclosure 源码 提取码:gqps

block 的定义以及 外部对象的捕获

探索

定义一个block 的代码,并捕获外部函数,去研究一下这个过程执行了哪些操作

1.通过 Debug => Debug Workflow => Always show Disassembly 断点查看block 的调用

在定义对象时,汇编会调用到 objc_retainBlock

->  0x100003d30 <+48>:  movq   %rcx, %rdi
    0x100003d33 <+51>:  callq  0x100003dc4               ; symbol stub for: objc_retainBlock

添加一个符号断点 Symbolic BreakPoint objc_retainBlock。 然后跳转执行

libobjc.A.dylib`objc_retainBlock:
->  0x7fff727aff83 <+0>: jmp    0x7fff727c6988            ; symbol stub for: _Block_copy

可以看到会执行 _Block_copy 的符号,另外在libobjc objc 的源码中查看 objc_retainBlock 的代码,会执行 _Block_copy函数

id objc_retainBlock(id x) {
    return (id)_Block_copy(x);
}

添加一个 符号断点 Symbolic BreakPoint _Block_copy 可以找到

libsystem_blocks.dylib`_Block_copy:
->  0x7fff739ab96b <+0>:   pushq  %rbp

后续可以通过 Hopper 取反汇编 libsystem_blocks 查看 _Block_copy 的代码。
这里获取到了 libclosure 提取码:ucu1 用来学习

_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
    // 将参入参数转换为 Block_layout 的结构体指针
    aBlock = (struct Block_layout *)arg;
    
    // 判断block的flag 是否 需要释放 
    if (aBlock->flags & BLOCK_NEEDS_FREE) {
        // latches on high
        latching_incr_int(&aBlock->flags);
        return aBlock;
    }
    // BLOCK_IS_GLOBAL 是在编译器确定的,如果时 全局的就返回,不处理
    else if (aBlock->flags & BLOCK_IS_GLOBAL) {
        return aBlock; // 不需要
    }
    else {
        // 如果是 栈函数 进行处理
        // Its a stack block.  Make a copy.
        
        // 创建一个 malloc 的空间,将 栈区 的数据拷贝的 堆 中
        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
        // 重置 refcount 的数量 以及 Deallocating 标志 为0  & ~ 。
        result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING);    // XXX not needed
        // 设置了 BLOCK_NEEDS_FREE 以及 BLOCK_DEALLOCATING 的数据
        result->flags |= BLOCK_NEEDS_FREE | 2;  // logical refcount 1
        
        // 代码块在编译时,当有捕获外部对象时,会在编译时,生成一个Block_Byref的结构体对象,用来存储相关的捕获对象的数据
        // _Block_call_copy_helper 这里就是将相关的数据 从栈区的数据 拷贝到 堆区的逻辑
        _Block_call_copy_helper(result, aBlock);
        // Set isa last so memory analysis tools see a fully-initialized object.
        result->isa = _NSConcreteMallocBlock;
        return result;
    }
}
  1. 会将传入的arg参数转换为 Block_layout 的结构体指针。说明 Block_layout 就是block 的结构。flag 会再编译器确定一些数据。
  2. BLOCK_NEEDS_FREE 是在运行时确定的数据。 第一次进入是会是0.然后再 stack 拷贝到 malloc 之后,会设置。其实就是如果已经堆区的block。对其的操作就只即将引用计数增加
  3. BLOCK_IS_GLOBAL 全局的 block 就直接返回,不做处理
  4. 处理栈的block
    1. 分配堆的空间,然后将栈中的block 数据拷贝到堆
    2. 设置 函数指针,
    3. 重置 BLOCK_REFCOUNT_MASK 以及 BLOCK_DEALLOCATING 的数据
    4. 以及设置 上面提到的 BLOCK_NEEDS_FREE
    5. 执行 _Block_call_copy_helper 对捕获的外部逻辑的处理
    6. 将isa 指向 _NSConcreteMallocBlock。
    7. 返回新创建的 堆区 的block

主要三个部分

  1. 如果已经是堆区的block( BLOCK_NEEDS_FREE) 就处理引用计数
  2. 如果时全局的block 不去处理
  3. 如果时stack block 就将stack 处理拷贝到 堆区转换成 malloc block

latching_incr_int

主要是对 BLOCK_REFCOUNT数据的处理。可以看下flag 的位域布局。 BLOCK_REFCOUNT 是从第二位开始,加一就是 flag + 2

static int32_t latching_incr_int(volatile int32_t *where) {
    while (1) {
        int32_t old_value = *where;
        // 如果数据就是掩码,就是美数据
        if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
            return BLOCK_REFCOUNT_MASK;
        }
        //  原子操作, 比较和交换 
        // 加2 是因为 最低的标识为是 BLOCK_DEALLOCATING,引用计数的位域是从第2位开始,所以flag + 2 其实是 引用计数加1 
        if (OSAtomicCompareAndSwapInt(old_value, old_value+2, where)) {
            return old_value+2; // 
        }
    }
}
#define OSAtomicCompareAndSwapInt(_Old, _New, _Ptr) __sync_bool_compare_and_swap(_Ptr, _Old, _New)

enum {
    BLOCK_DEALLOCATING =      (0x0001),  // runtime 是否在 deallocating 1个位域
    // 0xfffe   0b1111 1111 1111 1110 引用计数的掩码,就是 2到16 位域的数据
    BLOCK_REFCOUNT_MASK =     (0xfffe),  // runtime  从0x2开始到 0xfffff 的位域  0x1 表示的是deallocating
    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 是否是 Global
    BLOCK_USE_STRET =         (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE 
    BLOCK_HAS_SIGNATURE  =    (1 << 30), // compiler 是否有签名 -> hook 用
    BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31)  // compiler
};


_Block_call_copy_helper

  1. 获取到 block 中的desc (在编译器确定的数据),然后执行一次。根据相关结构体的定义以及使用xrun 半年以后的cpp文件, 可以找到相关的 copy 函数和 dispose 函数
  2. 编译中掉哟蝈
static void _Block_call_copy_helper(void *result, struct Block_layout *aBlock)
{
    // 先去判断有没有 descriptor2 就是有没有 拷贝和释放函数
    struct Block_descriptor_2 *desc = _Block_descriptor_2(aBlock);
    if (!desc) return;
    // 会调用编译器自动生成的copy 函数,
    (*desc->copy)(result, aBlock); // do fixup
}

// 例子
static void __main_block_copy_1(struct __main_block_impl_1*dst, struct __main_block_impl_1*src){
    // scr->p1 就是定义的代码块中 捕获的参数
    _Block_object_assign( (void*)&dst->p1, (void*)src->p1, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->p1, 3/*BLOCK_FIELD_IS_OBJECT*/);
_Block_object_dispose((void*)src->p2, 8/*BLOCK_FIELD_IS_BYREF*/);
}

  1. 在block 的copy函数中,对于每一个捕获的变量,编译器都会生成一个。 最后一个是 捕获对象的类型 _Block_object_dispose((void)src->p1, 3/BLOCK_FIELD_IS_OBJECT*/);
  2. 在block的 despose 函数中,则会对每一个变量执行 _Block_object_dispose 函数, 同样参入参数的类型

_Block_object_assign

// block 捕获变量的类型标志
enum {
    // see function implementation for a more complete description of these fields and combinations
    BLOCK_FIELD_IS_OBJECT   =  3,  // id, NSObject, __attribute__((NSObject)), block, 
    ...
    BLOCK_FIELD_IS_BLOCK    =  7,  // a block 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.
};

// 仅对上面面的几种类型的参数进行操作
enum {
    BLOCK_ALL_COPY_DISPOSE_FLAGS = 
        BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_BLOCK | BLOCK_FIELD_IS_BYREF |
        BLOCK_FIELD_IS_WEAK | BLOCK_BYREF_CALLER
};

// When Blocks or Block_byrefs hold objects then their copy routine helpers use this entry point
// to do the assignment.

// __block 变量 destArg malloc取的对象指针地址, 栈区block 补货变量的指针, flag
void _Block_object_assign(void *destArg, const void *object, const int flags) {
    const void **dest = (const void **)destArg;
   
    // 仅对不会类型的参数进行操作 对象, block,__block 定义的变量,__weak, de duixiang
    switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
    
        
        /******* 类似
        id object = ...;
        [^{ object; } copy];
        ********/
        // objc 指针地址 weakSelf (self)
        // arc
        _Block_retain_object(object);
            // 持有
        // 将objc的指针存入到 堆取复制对象的地址。有一个引用
        *dest = object;
        break;

      case BLOCK_FIELD_IS_BLOCK:
        /*******
        void (^object)(void) = ...;
        [^{ object; } copy];
        ********/
            
        // block 被一个 block 捕获
        // 那么被捕获的block 也要进行 _Block_copy 一边
        *dest = _Block_copy(object);
        break;
    
      case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK: // 
      case BLOCK_FIELD_IS_BYREF: // 如果是 __block 修饰的变量
        /*******
         // 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];
         ********/
        // 执行 __ _Block_byref_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;
    }
}

_Block_byref_copy

在内存中创建 __block 修饰对象编译时 创建的结构体,然后拷贝

static struct Block_byref *_Block_byref_copy(const void *arg) {
    
    // Block_byref  结构体
    struct Block_byref *src = (struct Block_byref *)arg;
    
    if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
        // src points to stack
        // 创建新的copy
        struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
        copy->isa = NULL;
        //设置 BLOCK_BYREF_NEEDS_FREE
        // byref value 4 is logical refcount of 2: one for caller, one for stack
        copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
        
        // 问题 - block 内部 持有的 Block_byref 锁持有的对象 是不是同一个
        // 这里就是 __block 修饰后 外部变量和内部捕获变量一致所达到的方式,,src 的forwarding 指向的时新创建的 malloc 数据。
        copy->forwarding = copy; // patch heap copy to point to itself
        src->forwarding = copy;  // patch stack to point to heap copy
        
        copy->size = src->size;
        // 是否有copy 函数, 有就移动 key 函数以及个desctory 函数,
        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;

            // 在判断 移动 Block_byref_3 的数据
            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;
            }
            // 执行定义的keep 方法(编译确定)
            (*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
    // 如果已经是在内存中 (即之前已经在内存的数据里,就增加引用计数 -> 比如一个 __block 修饰的对象被多个block捕获)
    else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
        latching_incr_int(&src->forwarding->flags);
    }
    return src->forwarding;
}
  1. 如果 BLOCK_BYREF_HAS_COPY_DISPOSE 的情况,回去找到并调用 keep 方法
    通过 xrun 的命令,然后编译一个 捕获了 __block修饰的变量,然后就能找到 keep 和 dispose 函数的定义。这里是在 BLOCK_BYREF_HAS_COPY_DISPOSE 情况下。 根据编译的方法,会对捕获的对象数据调用_Block_object_assign 方法
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
 _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
static void __Block_byref_id_object_dispose_131(void *src) {
 _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}
  1. 如果没有 copy dispose 方法,则会之间创建一个内存地址,并拷贝复制 栈对应的数据
_Block_copy 总结:
  1. block 的拷贝: 当将block 复制给一个变量时,会执行 _Block_copy => 在堆创建 Block_layout 结构体,将栈区的数据拷贝进去
  2. 引入变量的 assign: 然后判断 Block_layout 变量是否有 编译确定的copy 函数,有的话,去执行,编译确定的copy函数其实就是 一次执行对所有拷贝变量的 _Block_object_assign. -> 对象就是直接引用,
    1. 引入的变量 如果是 __block 修饰的 变量,就会在编译器生成一个 Block_byref 的结构体类型,然后执行_Block_byref_copy 其中 的forward 操作是实现代码块内部可以修改外部数据的实现(因为外部访问的其实是forword 的指向的数据) (对于简单数据类型,直接复制新的内存数据)
    2. 其他的就是再对象地址上的 写入 传入的指针地址 或者 简单类型数据; *dest = object; 操作

相关结构体的定义

Block_layout

block 对应的结构体

struct Block_layout {
    void *isa; // 对象指针  global / malloc / statck 的 block类
    volatile int32_t flags; // contains ref count 包含block的 一些信息
    int32_t reserved;
    BlockInvokeFunction invoke;
    struct Block_descriptor_1 *descriptor; // 结构体的描述,关于签名,变量捕获相关的数据
    // imported variables
    // 后续的是引入的参数,就会根据
};

// block 的flag 中存储的信息,
// Values for Block_layout->flags to describe block objects
enum {
    BLOCK_DEALLOCATING =      (0x0001),  // runtime 是否在 deallocating
    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 是否是 Global
    BLOCK_USE_STRET =         (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE 
    BLOCK_HAS_SIGNATURE  =    (1 << 30), // compiler 是否有签名 -> hook 用
    BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31)  // compiler
};
// descriptor 指针指向的地址 其中size 时整个Block_descriptor的大小,后续的内存可能时 BLOCK_DESCRIPTOR_2 也可能时 BLOCK_DESCRIPTOR_3 在编译时确定
struct Block_descriptor_1 {
    uintptr_t reserved;
    // Block_descriptor_1 对象分配的内存大小。
    // 因为可能包含着 Block_descriptor_2  Block_descriptor_3 的数据
    uintptr_t size; 
};

// 可选
#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
};

可以配合xrun命令去理解 compiler 确定的一些数据

    Person *p1 = [[Person alloc] init];
    Block b2 = ^{
        NSLog(@"12");
        p1.name = @"asd";
    };


// 编译生成的数据
struct __main_block_impl_1 {
  struct __block_impl impl;
  struct __main_block_desc_1* Desc;
  Person *p1;
  __main_block_impl_1(void *fp, struct __main_block_desc_1 *desc, Person *_p1, int flags=0) : p1(_p1) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_1(struct __main_block_impl_1 *__cself) {
  Person *p1 = __cself->p1; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_gw_flhw88953gg0m9rcpg_y974w0000gn_T_main_8c5857_mi_1);
        ((void (*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)p1, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_gw_flhw88953gg0m9rcpg_y974w0000gn_T_main_8c5857_mi_2);
    }
static void __main_block_copy_1(struct __main_block_impl_1*dst, struct __main_block_impl_1*src) {_Block_object_assign((void*)&dst->p1, (void*)src->p1, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __main_block_dispose_1(struct __main_block_impl_1*src) {_Block_object_dispose((void*)src->p1, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __main_block_desc_1 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_1*, struct __main_block_impl_1*);
  void (*dispose)(struct __main_block_impl_1*);
} __main_block_desc_1_DATA = { 0, sizeof(struct __main_block_impl_1), __main_block_copy_1, __main_block_dispose_1};

Block_byref

__block 修饰的变量 会在编译器编译成一个Block_byref 的结构体。

// Values for Block_byref->flags to describe __block variables
enum {
    // Byref refcount must use the same bits as Block_layout's refcount.
    // BLOCK_DEALLOCATING =      (0x0001),  // runtime 是否正在析构
    // BLOCK_REFCOUNT_MASK =     (0xfffe),  // runtime 引用数的mask

    BLOCK_BYREF_LAYOUT_MASK =       (0xf << 28), // compiler  编译器确定
    BLOCK_BYREF_LAYOUT_EXTENDED =   (  1 << 28), // compiler 
    BLOCK_BYREF_LAYOUT_NON_OBJECT = (  2 << 28), // compiler
    BLOCK_BYREF_LAYOUT_STRONG =     (  3 << 28), // compiler
    BLOCK_BYREF_LAYOUT_WEAK =       (  4 << 28), // compiler 
    BLOCK_BYREF_LAYOUT_UNRETAINED = (  5 << 28), // compiler

    BLOCK_BYREF_IS_GC =             (  1 << 27), // runtime

    BLOCK_BYREF_HAS_COPY_DISPOSE =  (  1 << 25), // compiler
    BLOCK_BYREF_NEEDS_FREE =        (  1 << 24), // runtime
};

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

struct Block_byref_2 {
    // requires BLOCK_BYREF_HAS_COPY_DISPOSE
    BlockByrefKeepFunction byref_keep; // 结构体 __block  对象
    BlockByrefDestroyFunction byref_destroy;
};

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

__block 的实现

__block 修饰的对象,会在变异期间,生成一个对应的 Block_byref 结构体数据,其对应的是 BLOCK_FIELD_IS_BYREF 类型。

struct __Block_byref_a_0 {
  void *__isa;
__Block_byref_a_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 NSObject *a;
};

__attribute__((__blocks__(byref))) __Block_byref_a_0 a = { 
(void*)0, // isa
(__Block_byref_a_0 *)&a, // foawrd
33554432,  // flag 
sizeof(__Block_byref_a_0),  // size
__Block_byref_id_object_copy_131,  // byref_keep 函数 Block_byref_2
__Block_byref_id_object_dispose_131, // byref_destroy  Block_byref_2
((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"))  // 被捕获的对象数据 [[NSObject alloc] init]
};

而在外部调用 这个变量使用forward 转一层去获取

NSLog((NSString *)&__NSConstantStringImpl__var_folders_lr_r085xz2d6jjbsxfv0mq9qh5m0000gn_T_main_47e798_mi_2,(a.__forwarding->a));

所以__block 修饰的变量,只要代码块 内外的不同变量有相同的 forward 就可以是外部数据和代码块内部数据保持一致。就达到了代码块可以修改外部变量的效果。

可以参考上面 _Block_byref_copy 的源码

// 问题 - block 内部 持有的 Block_byref 锁持有的对象 是不是同一个
    // 这里就是 __block 修饰后 外部变量和内部捕获变量一致所达到的方式,,src 的forwarding 指向的时新创建的 malloc 数据。
    copy->forwarding = copy; // patch heap copy to point to itself
    src->forwarding = copy;  // patch stack to point to heap copy

相关文章

网友评论

      本文标题:block 的底层逻辑,变量的捕获, 以及__block 的底层

      本文链接:https://www.haomeiwen.com/subject/fvlmbktx.html