iOS底层探索26、Block 原理

一、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 变量
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342