美文网首页
OC底层探索26、Block 原理

OC底层探索26、Block 原理

作者: _zhang__ | 来源:发表于2020-11-12 23:58 被阅读0次

    一、block简介

    1、block的三种类型

    - (void)my_block {
        
        // <__NSGlobalBlock__: 0x106d6b038>
        void (^block)(int) = ^(int value) {
        };
        NSLog(@"%@",block);
        
        
        // <__NSMallocBlock__: 0x6000001cd620>
        int a = 11;
        void (^block2)(int) = ^(int value) {
            NSLog(@"%d",a);
        };
        NSLog(@"%@",block2);
        
        
        // <__NSStackBlock__: 0x7ffee0161078>
        NSLog(@"%@",^(void) {
            NSLog(@"%d",a);
        });
        void (^__weak block3)(void) = ^(){
            NSLog(@"%d",a);
        };
        NSLog(@"%@",block3);// <__NSStackBlock__: 0x7ffee0161048>
    }
    
    • NSGlobalBlock - 全局 block
    • NSMallocBlock - 堆 block
    • NSStackBlock - 栈 block

    2、block的循环引用

    2.1、引起循环引用的原因

    typedef void(^MyBlock)(void);
    
    @interface ViewController ()
    
    @property (nonatomic,copy) NSString *name;
    @property (nonatomic,copy) MyBlock block;
    
    @end
    //
    - (void)viewDidLoad {
        [super viewDidLoad];
        [self my_blockCircular];
    }
    - (void)my_blockCircular {
        
        // 造成循环引用 self 和 block 互相持有
        // self --持有--> block --持有--> self
        self.block = ^() {
            NSLog(@"%@",self.name);
        };
    }
    

    如上代码,因selfblock的互相持有造成了循环引用。 --> __weak解决。

    2.2、循环引用的解决方案

    2.1.1、方案 1 - __weak

    代码修改如下:

    __weak typeof(self)weakSelf = self;
        /**
         self -> block -> weakSelf -> self,持有关系中加了中介 weakSelf
         weakSelf 在弱引用表中,不会对 self 的引用计数加一处理
         */
        self.block = ^ {
            NSLog(@"%@",weakSelf.name);
        };
    

    __weak是如何解决的呢?如何验证__weak是弱引用,不会持有对象对引用计数加一?

    1)__weak的原理

    OC底层探索27、weak 原理分析

    2)__weak解决循环引用时的一个问题

    示例代码如下,若block中执行耗时异步任务,在dealloc后才执行,self已销毁:

    self.block = ^ {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                
                NSLog(@"%@",weakSelf.name);
            });
    
        };
        self.block();
        /** 输出
         dealloc 走了
         (null) // 这里self已经销毁了才执行了block内的任务
         */
    

    造成此问题原因:self的生命周期没有得到合适的保障:
    selfwself弱引用,引用计数不会变,所以生命周期和不被block捕获时一样。
    修改代码修改如下:

        self.block = ^ {
             __strong typeof(weakSelf)strongSelf = weakSelf;
            
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
               
                NSLog(@"%@",strongSelf.name);
            });
        };
        self.block();
        /** 输出
         张三
         dealloc 走了
         */
    

    __strong为何可以解决,如上代码,strongSelfweakSelf进行了强持有,其作用域是block内部,当block任务执行完毕后,strongSelf变量被系统释放,weakSelfself也就释放了。

    2.1.2、方案 2 - 手动释放

    代码如下:

    // 解决方案 2 -- 手动释放
        __block ViewController *vc = self;
        self.block = ^{
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"%@",vc.name);
                vc = nil;// 不手动置为 nil 循环引用则仍在
            });
        };
    

    上述代码,vc变量 在比那辆被捕获到 block 内,增加了一个强持有,是不会释放的,而我们通过手动将self置为nil,从而释放触发 dealloc

    2.1.3、方案 3 - 临时变量

    已知循环引用的打破方式有2中,即selfblock 的任意一方对另一方弱引用便可打破,但block是不能使用weak修饰的,weak修饰的属性刚创建就会被释放掉。那么只要不持有即可解决,我们仍是通过中介者模式,可以通过代理、临时变量等方式处理,示例代码如下:

    typedef void(^MyBlock2)(ViewController *vc);
    
    // 解决方案 3 -- 临时变量
        self.block2 = ^(ViewController *vc) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"%@",vc.name);
            });
        };
        self.block2(self);
    

    vc作为函数形参 作为临时变量被压栈,block不会对self持有.

    2.1.4、方案 4 - proxy

    二、block 本质

    1、block 编译后结构分析

    main.m 中添加代码,文件clang编译后如下:

            void (^block)(void) = ^{
               printf("my_block_test");
            };
            block();
    

    clang编译:

    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      // 结构体构造函数
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;// 函数式 编程
        Desc = desc;
      }
    };
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
        printf("my_block_test");
    }
    
    static struct __my_func_block_desc_0 {
        size_t reserved;
        size_t Block_size;
    } __my_func_block_desc_0_DATA = { 0, sizeof(struct __my_func_block_impl_0)};
    
    void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0,&__main_block_desc_0_DATA));
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    // 对上面2行代码简化,去掉类型强转:
    void (*block)(void) = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA); // __main_block_impl_0 函数
    block->FuncPtr(block);// block中 FuncPtr() 函数 的调用
    /** block->FuncPtr(block)
    这里FuncPtr(),也验证了为何block必须要调用 --> 函数的调用。
    block作为参数又传给FuncPtr 即 函数__main_block_func_0:
    类似objc_msgSenf(id self, SEL _cmd)隐藏参数,函数__main_block_func_0()可使用block结构体内的任何成员.
    */
    

    block本质是结构体,内部有其构造函数从而进行函数调用 --> block的代码块回调.

    2、block 的源码

    找到 block 的源码

    打开debug汇编,测试代码如下,block声明处打断点:

    - (void)my_block_copy {
        
        // block 最初编译读取的是个栈 block
        // 运行时经过 _Block_copy --> 成为堆 block
        // stack_block --> _Block_copy --> malloc_block
        int a = 10;
        void (^block)(void) = ^{
            
            NSLog(@"%d",a);
        };
        block();
    }
    

    运行工程:

    image.png

    添加符号断点objc_retainBlock,继续执行:

    image.png
    block源码位置在libsystem_blocks.dylib, libclosure-74 源码.

    2.1、Block_layout

    --> block 在底层真正的结构:struct Block_layout {}

    struct Block_layout {
        void *isa;
        volatile int32_t flags; // contains ref count
        int32_t reserved;
        BlockInvokeFunction invoke;
        struct Block_descriptor_1 *descriptor; // 描述符号
        // imported variables
    };
    

    2.1.1、flags值 - Block_layout->flags

    用于描述块对象的标志 :

    // 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
    };
    
    // 每个 block 都有
    #define BLOCK_DESCRIPTOR_1 1
    struct Block_descriptor_1 {
        uintptr_t reserved;
        uintptr_t size;
    };
    // 2、3 根据 flags 判断是否有
    // 可选 并非每个block都有
    #define BLOCK_DESCRIPTOR_2 1
    struct Block_descriptor_2 {
        // requires BLOCK_HAS_COPY_DISPOSE
        BlockCopyFunction copy;
        BlockDisposeFunction dispose;
    };
    // 可选 并非每个block都有
    #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
    };
    

    搜索Block_descriptor_2: -->Block_descriptor_2Block_descriptor_3,2者存在与否的判断 代码如下:

    /****************************************************************************
    Accessors for block descriptor fields
    *****************************************************************************/
    #if 0
    static struct Block_descriptor_1 * _Block_descriptor_1(struct Block_layout *aBlock)
    {
        return aBlock->descriptor;
    }
    #endif
    
    static struct Block_descriptor_2 * _Block_descriptor_2(struct Block_layout *aBlock)
    {
        // 没copy 直接返回 null
        if (! (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)) return NULL;
        // 获取Block_descriptor_2 --> 通过Block_descriptor_1进行内存平移
        uint8_t *desc = (uint8_t *)aBlock->descriptor;
        desc += sizeof(struct Block_descriptor_1);
        return (struct Block_descriptor_2 *)desc;
    }
    
    static struct Block_descriptor_3 * _Block_descriptor_3(struct Block_layout *aBlock)
    {
        // 没签名 直接返回 null
        if (! (aBlock->flags & BLOCK_HAS_SIGNATURE)) return NULL;
        uint8_t *desc = (uint8_t *)aBlock->descriptor;
        // 获取Block_descriptor_2 --> 通过Block_descriptor_1进行内存平移
        desc += sizeof(struct Block_descriptor_1);
        if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE) {
            // 有 2 则再加上2 进行内存平移
            desc += sizeof(struct Block_descriptor_2);
        }
        return (struct Block_descriptor_3 *)desc;
    }
    

    Block_descriptor_? 的获取:

    1. 获取Block_descriptor_2 --> 通过 Block_descriptor_1 进行内存平移得到;
    2. 获取Block_descriptor_2 --> 通过 Block_descriptor_1 + Block_descriptor_2(有2则加无则只1)进行内存平移得到.

    2.1.2、Block_descriptor_2Block_descriptor_3的存在与否

    1)有 Block_descriptor_3
    1.1)全局block的信息情况
        void (^block)(void) = ^{
            NSLog(@"hello");
        };
        block();
    
    image.png
    1.2)栈、堆 block的信息情况
    - (void)my_block_copy {
    
        int a = 10;
        void (^block)(void) = ^{
    
            NSLog(@"%d",a);
        };
        block();
    }
    // BLOCK_HAS_SIGNATURE & flag  有值   —>   有 descriptor_3
    
    image.png image.png

    运行时,栈block通过_Block_copy ,copy出一份到堆,得到堆block.详细过程基按文章下面copy源码分析。

    2)Block_descriptor_1 & 2 & 3均存在
    - (void)my_block_copy {
    
        __block int a = 10;
        void (^block)(void) = ^{
            a++;
            NSLog(@"%d",a);
        };
        block();
    }
    
    image.png

    3.2、关于 block 的签名

    由上面对block的结构分析,已知每个block的签名是必然会有的。下面我们通过内存来读取签名:

    (lldb) po 0x00006000039959e0
    <__NSMallocBlock__: 0x6000039959e0>
     signature: "v8@?0"
     invoke   : 0x100ce4f00 (/Users/domy/Library/Developer/CoreSimulator/Devices/329A1FCB-1ADA-46B4-AD1B-68A6ECA79F77/data/Containers/Bundle/Application/3337EFE6-31C2-4F3A-B963-26EC9C4DD7A8/Demo_Block_Copy.app/Demo_Block_Copy`__31-[ViewController my_block_copy]_block_invoke)
    
    (lldb) x/4gx 0x00006000039959e0 
    0x6000039959e0: 0x00007fff89ea07a0 0x00000000c1000002
    0x6000039959f0: 0x0000000100ce4f00 0x0000000100ce7020
    (lldb) x/8gx 0x0000000100ce7020
    0x100ce7020: 0x0000000000000000 0x0000000000000024
    0x100ce7030: 0x0000000100ce624b 0x0000000100ce62e1
    0x100ce7040: 0x00007fff87c51128 0x00000000000007c8
    0x100ce7050: 0x0000000100ce6248 0x0000000000000002
    (lldb) p/x 1 << 25
    (int) $10 = 0x02000000
    (lldb) p 0x02000000 & 0x00000000c1000002
    (unsigned int) $11 = 0
    (lldb) p/x 1 << 30
    (int) $12 = 0x40000000
    (lldb) p 0x40000000 & 0x00000000c1000002
    (unsigned int) $13 = 1073741824
    (lldb) po 0x0000000100ce624b // 没有2 得到3的签名 则 1平移 2*8=16
    4308492875
    
    (lldb) po (char  *)0x0000000100ce624b
    "v8@?0"
    
    (lldb) 
    
    image.png

    block的签名:

    (lldb) po [NSMethodSignature signatureWithObjCTypes:"v8@?0"] 
    <NSMethodSignature: 0x6000039deac0>
        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}
            modifiers {}
            frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0}
            memory {offset = 0, size = 8}
    

    3.3、block 的内存栈 --> 堆变化 - _Block_copy

    3.3.1、汇编分析copy流程

    重新运行工程,如下图位置,读取寄存器register read:

    image.png

    当前block还是栈类型,继续执行,进入到_Block_copy,将断点打在下图位置(即: block完成copy)处:

    image.png

    blockcopy 流程:编译时是stack_block --> 运行时经过_Block_copy --> malloc_block

    3.3.2、_Block_copy源码分析

    // Copy, or bump refcount, of a block.  If really copying, call the copy helper if present.
    // 栈 -> 堆 拷贝过程
    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; // 全局 不需要copy 直接返回自己
        }
        else { // 栈 
            // Its a stack block.  Make a copy.
            // 开辟堆空间
            struct Block_layout *result =
                (struct Block_layout *)malloc(aBlock->descriptor->size);
            if (!result) return NULL;
            // 内存移动, 将 aBlock 整个 copy 到 result
            memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
    #if __has_feature(ptrauth_calls) 
            // Resign the invoke pointer as it uses address authentication.
            // invoke调用 
            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);
            /**
             static void _Block_call_copy_helper(void *result, struct Block_layout *aBlock)
             {
                // BLOCK_HAS_COPY_DISPOSE 是否标记 copy
                 struct Block_descriptor_2 *desc = _Block_descriptor_2(aBlock);
                 if (!desc) return;
    
                 (*desc->copy)(result, aBlock); // do fixup
             }
             */
    
            // Set isa last so memory analysis tools see a fully-initialized object.
            // 最后设置 isa 为堆block  MallocBlock
            result->isa = _NSConcreteMallocBlock;
            return result;
        }
    }
    

    三、block捕获外部变量 原理分析

    1、block 捕获外部变量初探

    1.1、外部变量的调用

    main.m文件中代码如下:

    static int A = 10;
    int B = 20;
    
    void my_func () {
        int a = 123;
        void (^block)(void) = ^{
            A++;
            B++;
            printf("%d - %d - %d",a,A,B);
        };
        block();
    }
    

    clang编译:

    struct __my_func_block_impl_0 {
        struct __block_impl impl;
        struct __my_func_block_desc_0* Desc;
        int a;// 编译时 自动生成创建了 a 变量 
        // a(_a) : a = _a --> _a即传来的a
        __my_func_block_impl_0(void *fp, struct __my_func_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
            impl.isa = &_NSConcreteStackBlock;// 编译时,栈block
            impl.Flags = flags;
            impl.FuncPtr = fp;
            Desc = desc;
        }
    };
    static void __my_func_block_func_0(struct __my_func_block_impl_0 *__cself) {
        // 赋值 int a --> 值copy --> 2个a变量值相同
        int a = __cself->a; // bound by copy
        // 这里也验证了,为何外部变量不可直接在block内部使用:
        // 因为内部有声明新的值相同的变量 --> 若block内部直接用外部变量则会造成编译器的代码歧义,报错
        A++;
        B++;
        printf("%d - %d - %d",a,A,B);
    }
    
    static struct __my_func_block_desc_0 {
        size_t reserved;
        size_t Block_size;
    } __my_func_block_desc_0_DATA = { 0, sizeof(struct __my_func_block_impl_0)};
    
    void my_func () {
        int a = 123;
        // a 作为参数传给函数 __my_func_block_impl_0
        void (*block)(void) = ((void (*)())&__my_func_block_impl_0((void *)__my_func_block_func_0, &__my_func_block_desc_0_DATA, a));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    } 
    
    1. block对全局or静态变量是不进行捕获操作的,它可直接对全局静态变量进行使用;
    2. block捕获局部变量:编译时内部生成一个新的进行值copy的变量。

    1.2、外部变量 修改+使用

    __block分析:

    void my_func () {
        __block int a = 123;
        void (^block)(void) = ^{
            a++;
            printf("%d",a);
        };
        block();
    }
    

    clang:

    // a 封装成对象
    struct __Block_byref_a_0 {
        void *__isa;
        __Block_byref_a_0 *__forwarding;
        int __flags;
        int __size;
        int a;
    };
    
    struct __my_func_block_impl_0 {
        struct __block_impl impl;
        struct __my_func_block_desc_0* Desc;
        __Block_byref_a_0 *a; // by ref
        __my_func_block_impl_0(void *fp, struct __my_func_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;
        }
    };
    static void __my_func_block_func_0(struct __my_func_block_impl_0 *__cself) {
        // 指针 copy
        __Block_byref_a_0 *a = __cself->a; // bound by ref
        
        (a->__forwarding->a)++;
        printf("%d",(a->__forwarding->a));
    }
    static void __my_func_block_copy_0(struct __my_func_block_impl_0*dst, struct __my_func_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static void __my_func_block_dispose_0(struct __my_func_block_impl_0*src) {_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static struct __my_func_block_desc_0 {
        size_t reserved;
        size_t Block_size;
        void (*copy)(struct __my_func_block_impl_0*, struct __my_func_block_impl_0*);
        void (*dispose)(struct __my_func_block_impl_0*);
    } __my_func_block_desc_0_DATA = { 0, sizeof(struct __my_func_block_impl_0), __my_func_block_copy_0, __my_func_block_dispose_0};
    void my_func () {
        // a 封装成一个对象 --> __Block_byref_a_0 结构体
        __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {
            (void*)0,// isa
            (__Block_byref_a_0 *)&a,// __forwarding
            0,// flags
            sizeof(__Block_byref_a_0),// size
            123// a的值
        };
        // (__Block_byref_a_0 *)&a :传的 a 的地址
        void (*block)(void) = ((void (*)())&__my_func_block_impl_0((void *)__my_func_block_func_0, &__my_func_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    

    编译时,__block将变量a封装成了一个对象__Block_byref_a_0,函数执行时(block内部修改操作),传入的是指针,so,修改操控的是block外部传入的变量a.
    __block做了什么:

    1. 将变量封装成相应结构体 - __Block_byref_a_0 {};
    2. 保存原始变量的指针和值;
    3. 传递指针地址给block.

    2、block 捕获外界变量 原理分析

    /*******************************************************
    
    Entry points used by the compiler - the real API!
    
    
    A Block can reference four different kinds of things that require help when the Block is copied to the heap.
    1) C++ stack based objects
    2) References to Objective-C objects
    3) Other Blocks
    4) __block variables
    
    In these cases helper functions are synthesized by the compiler for use in Block_copy and Block_release, called the copy and dispose helpers.  The copy helper emits a call to the C++ const copy constructor for C++ stack based objects and for the rest calls into the runtime support function _Block_object_assign.  The dispose helper has a call to the C++ destructor for case 1 and a call into _Block_object_dispose for the rest.
    
    The flags parameter of _Block_object_assign and _Block_object_dispose is set to
        * BLOCK_FIELD_IS_OBJECT (3), for the case of an Objective-C Object,
        * BLOCK_FIELD_IS_BLOCK (7), for the case of another Block, and
        * BLOCK_FIELD_IS_BYREF (8), for the case of a __block variable.
    If the __block variable is marked weak the compiler also or's in BLOCK_FIELD_IS_WEAK (16)
    
    So the Block copy/dispose helpers should only ever generate the four flag values of 3, 7, 8, and 24.
    
    When  a __block variable is either a C++ object, an Objective-C object, or another Block then the compiler also generates copy/dispose helper functions.  Similarly to the Block copy helper, the "__block" copy helper (formerly and still a.k.a. "byref" copy helper) will do a C++ copy constructor (not a const one though!) and the dispose helper will do the destructor.  And similarly the helpers will call into the same two support functions with the same values for objects and Blocks with the additional BLOCK_BYREF_CALLER (128) bit of information supplied.
    
    So the __block copy/dispose helpers will generate flag values of 3 or 7 for objects and Blocks respectively, with BLOCK_FIELD_IS_WEAK (16) or'ed as appropriate and always 128 or'd in, for the following set of possibilities:
        __block id                   128+3       (0x83)
        __block (^Block)             128+7       (0x87)
        __weak __block id            128+3+16    (0x93)
        __weak __block (^Block)      128+7+16    (0x97)
            
    
    ********************************************************/
    
    //
    // When Blocks or Block_byrefs hold objects then their copy routine helpers use this entry point
    // to do the assignment.
    // hold objects - 自动捕获到变量
    // lgname
    
    // __block 变量
    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 指针地址 weakSelf (self)
                // arc
            _Block_retain_object(object);
                // 持有
            *dest = object;
            break;
    
          case BLOCK_FIELD_IS_BLOCK:
            /*******
            void (^object)(void) = ...;
            [^{ object; } copy];
            ********/
                
                // block 被一个 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;
        }
    }
    
    // When Blocks or Block_byrefs hold objects their destroy helper routines call this entry point
    // to help dispose of the contents
    void _Block_object_dispose(const void *object, const int flags) {
        switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
          case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
          case BLOCK_FIELD_IS_BYREF:
            // get rid of the __block data structure held in a Block
            _Block_byref_release(object);
            break;
          case BLOCK_FIELD_IS_BLOCK:
            _Block_release(object);
            break;
          case BLOCK_FIELD_IS_OBJECT:
            _Block_release_object(object);
            break;
          case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
          case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
          case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
          case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK  | BLOCK_FIELD_IS_WEAK:
            break;
          default:
            break;
        }
    }
    

    block 可捕获:

    1. 基于c++堆栈的对象
    2. 引用 OC 对象
    3. 其他 block 块
    4. __block 变量

    相关文章

      网友评论

          本文标题:OC底层探索26、Block 原理

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