iOS中的Block底层原理解析

Block的本质

Block 对象是c语言的语法和运行时结构。很像c函数,但是执行代码的时会使用栈区和堆区的变量。因此,一个block执行的时候,它所维持的这些状态和数据会影响它的行为。虽然block是C语言语法,并且能在c和C++中使用,但是他一个OC对象:

来自官方文档.jpeg

它的底层结构是一个结构体,结构体中的函数指针指向block的函数实现,block()调用的就是这个函数。

Block的类型

跟普通对象一样,Block也有isa指针指向相应的类型。Block有三个类型(NSGlobalBlock,NSMallocBlock,NSStackBlock)。

全局Block(NSGlobalBlock)

Block在不引用外部变量或只引用全局变量或者静态变量就是全局Block。代码示例:

    //不引用任何变量
    void (^block1)(void) = ^{
    };
    NSLog(@"block1:%@",block1);
    
    //引用静态变量
   static int c = 0;
   void (^block2)(void) = ^{
       c = 1;
   };
   NSLog(@"block2:%@",block2);

打印结果:

2021-07-21 17:23:53.944309+0800 001---BlockDemo[32880:11497834] block1:<NSGlobalBlock: 0x10c92a0a0>
2021-07-21 17:23:53.944616+0800 001---BlockDemo[32880:11497834] block2:<NSGlobalBlock: 0x10c92a0c0>

堆Block(NSMallocBlock)

引用了局部变量,发生copy操作之后就变成堆block。代码示例:

   //引用局部变量a
    int a = 0;
    //这里默认是强引用,发生了copy操作
    void (^block3)(void) = ^{
       NSLog(@"a:%@",@(a));
    };
    NSLog(@"block3:%@",block3);

打印结果:

2021-07-21 17:32:21.386941+0800 001---BlockDemo[32934:11503105] block3:<NSMallocBlock: 0x600002227cf0>

栈Block(NSStackBlock)

引用了局部变量,未发生copy操作。代码示例:

    //引用局部变量a
    int a = 0;
    //这里__weak是弱引用,未发生了copy操作
    void (^__weak block4)(void) = ^{
       NSLog(@"a:%@",@(a));
    };
    NSLog(@"block4:%@",block4);

打印结果:

2021-07-21 17:35:39.843363+0800 001---BlockDemo[32992:11509042] block4:<NSStackBlock: 0x7ffee2080488>

Block的底层结构-Block layout

Block_layout是block结构体的底层结构,其源码如下:

struct Block_layout {
    void *isa; //
    volatile int32_t flags; // contains ref count
    int32_t reserved;
    BlockInvokeFunction invoke;// block()通过它调用函数
    struct Block_descriptor_1 *descriptor; //
    // imported variables
};
  • isa 指针指向Block的类型;
  • invoke 是Block函数,Block调用实际上就是调用的invoke
  • flags 是个标记位,用于标记Block的类型、状态等
//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
//};
  • descriptor是可变属性,而且是递增的。默认的是:
#define BLOCK_DESCRIPTOR_1 1
struct Block_descriptor_1 {
    uintptr_t reserved;
    uintptr_t size;
};

当block引用对象时,就会新增如下结构:

#define BLOCK_DESCRIPTOR_2 1
struct Block_descriptor_2 {
    // requires BLOCK_HAS_COPY_DISPOSE
    BlockCopyFunction copy;
    BlockDisposeFunction dispose;
};

BlockCopyFunction copy用于保存对象的copy函数,因为如果block引用对象,当Block发生copy时,引用的对象也要copy(Block copy时有解析)。此时的descriptor相当于:

struct Block_descriptor {
    uintptr_t reserved;
    uintptr_t size;
    BlockCopyFunction copy;
    BlockDisposeFunction dispose;
};
//依据是编译成C++代码时增加了:
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

当block有签名时,增加Block_descriptor_3:

#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捕获外部变量

普通变量的捕获

Block捕获外部普通变量(不是__block等修饰的变量)会自动生成一个属性来保存。接下来我们通过将main.c文件编译成main.cpp文件查看底层c++代码进行验证。首先在main函数实现如下block代码:

#import <UIKit/UIKit.h>
int main(int argc, char * argv[]) {
    int a = 11;
    void (^block)(void) = ^(void){
        NSLog(@"%@",@(a));
    };
    block();
}

通过命令xcrun -sdk iphonesimulator clang -arch x86_64 -rewrite-objc main.m 编译成main.cpp文件,打开,找到main函数对应的c++代码,如下:

int main(int argc, char * argv[]) {
    int a = 11;
  //这一行是block的创建
    void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
    //这一行是block调用
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
//为了便于分析,把类型转换符号去掉,可以简化成:
void (*block)(void) = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, a);
    //这一行是block调用,可以看到block调用的时候他会把自身作为参数再次传到函数里面,这一步的用处在后面的__main_block_func_0会讲解
block->FuncPtr(block);
}
  • __main_block_impl_0结构体分析
    __main_block_impl_0是Block编译时自动生成的结构体,Block的创建实际上就是这个结构调用了构造函数创建的,其结构体代码如下:
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int a; //自动生成a属性来保存捕获的变量a
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {// 构造函数
    impl.isa = &_NSConcreteStackBlock;//isa指针
    impl.Flags = flags;//
    impl.FuncPtr = fp;//fp函数作为参数传入,保存在FuncPtr指针,这样block()才能调用函数
    Desc = desc;
  }
};

在这里可以看到__block_impl的构造函数跟我们前面的Block_layout结构体是对应关系:

isa 对应 isa
Flags 对应falgs
FuncPtr 对应 invoke
Desc 对应 descriptor

  • 为捕获的变量自动生成属性以及descriptor的确定
    __main_block_impl_0__block_impl的基础上增加了属性int a用于捕获的外部变量a;同时还增加可变属性__main_block_desc_0* Desc,这个Desc的可变怎么体现呢?比如上面的demo把a是一个值类型(int),它对应的__main_block_desc_0的结构如下:
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

但是如果把a换成对象类型,比如:

    NSObject *obj = [[NSObject alloc] init];
    void (^block1)(void) = ^{
        NSLog(@"%@",obj);
    };
    block1();

这时候编译的到的_main_block_desc_0变成:

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

这与我们前面对Block_layout的分析是一致的,它增加了copydispose方法。因为对象类型需要这两个方法而值类型不需要。

  • 被捕获的外部变量的访问
    __main_block_func_0就是block()调用的函数的实现。我们创建Block的时候并没有传入函数名称,但是Block却自动生成一个函数实现,这也是block被称为匿名函数的由来。__main_block_func_0它的实现代码:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
//函数内又会自定义一个临时变量copy了block结构体中cself->a的值
  int a = __cself->a; // // bound by copy
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_kz_91163dcd57j_zw_xyry904bc0000gn_T_main_c2c542_mi_0,((NSNumber *(*)(Class, SEL, int))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), (int)(a)));
    }

通过源码我们发现__main_block_func_0的参数实际上是Block结构体本身,这样也就解释了为什么我们能在Block代码块里面访问其结构体__main_block_impl_0中的属性了,比如这里的int a属性,这里面的a实际上已经不是block外面的变量a,而是__main_block_func_0自定义的临时变量用于接收Block结构体属性a。这同时也解释了为什么在Block里面无法对a进行修改。

那为什么__block修饰的变量可以被修改呢?它是如何被捕获的?加下来我们看看__block修饰的变量的捕获。

__block变量的捕获

用__block对int a进行修饰,同时在block内对a++操作,研究一下__block的实现原理及a为何能被修改。代码如下:

__block int a = 11;
    void (^block)(void) = ^(void){
        a++;
        NSLog(@"%@",@(a));
    };
    block();

重复上面的编译步骤,得到编译后main函数的c++代码如下:

int main(int argc, char * argv[]) {

    __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 11};
    void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

为方便阅读,简化成如下代码:

int main(int argc, char * argv[]) {

    __Block_byref_a_0 a = {
        (void*)0,
        (__Block_byref_a_0 *)&a,
        0,
        sizeof(__Block_byref_a_0),
        11
        
    };
    void (*block)(void) = __main_block_impl_0(
                                              _main_block_func_0,
                                              &__main_block_desc_0_DATA,
                                              (__Block_byref_a_0 *)&a,
                                              570425344
                                              );
   block->FuncPtr(block);
}
  • __main_block_impl_0结构体分析
    此时的Block的结构体__main_block_impl_0 代码如下:
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_a_0 *a; // by ref 
  __main_block_impl_0(void *fp, struct __main_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;
  }
};

此时的__main_block_impl_0和捕获普通对象时的结构基本是一样的,唯一不同在于对外部变量的捕获。

  • 自动生成一个结构体属性用于接收外部__block变量
    通过上面的代码可以看出这段代码比前面的多了一个结构体__Block_byref_a_0,这个结构体实际上Block用来捕获__block int a 变量的,其结构体源码如下:
struct __Block_byref_a_0 {
  void *__isa;
__Block_byref_a_0 *__forwarding;// 保存变量a的指针
 int __flags;
 int __size;
 int a;// 保存变量a的值
};
  • 为什么__block 变量可以在Block里面修改
    由代码可以看出Block不仅捕获__block int a 变量的值,还捕获了a的指针,通过__Block_byref_a_0结构体分别保存在属性int a 和 __Block_byref_a_0 *__forwarding中, 保存原始变量 指针 - 值,拿到变量a的地址,所以可以通过指针进行修改。
  • __block变量在Block函数中如何访问?
    此时__main_block_func_0函数实现也变成如下状态:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_a_0 *a = __cself->a; // bound by ref,这里是指针拷贝

        (a->__forwarding->a)++; //通过__forwarding指针访问a变量并可以修改
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_kz_91163dcd57j_zw_xyry904bc0000gn_T_main_2b95a8_mi_0,((NSNumber *(*)(Class, SEL, int))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), (int)((a->__forwarding->a))));
    }

可以看到__main_block_func_0函数里面变量a的指针进行了拷贝,并且通过a->__forwarding->a访问变量a并进行修改,这样Block内部和外部指向同一个地址,所以可以修改外部变量。

Block的copy原理解析

什么时候进行Copy?

通过汇编调试可以找到Block进行copy的地方 。以下通过以下两步汇编断点调试,跳到_Block_copy的代码实现:

       NSObject *obj = [[NSObject alloc] init];
    void (^block1)(void) = ^{
//        NSLog(@"a:%@",@(a));
        NSLog(@"%@",obj);
    };
    block1();
Block断点调试.jpg

block源码.jpg

根据提示可以看出Block源码出自libsystem_blocks.dylib动态库,Block的copy操作是在_Block_copy函数中完成的。

NSStackBlock变成NSMallocBlock过程演示

栈Block进行copy操作之后就变成堆Block的。接下来我们通过汇编调试验证这一过程。通过在_Block_copy调用前和调用结束打断点分析block类型变化来演示:

BlockDemo.jpg

运行到断点位置,然后进入汇编调试,然后添加符号objc_retainBlock断点,继续运行:

符号断点.png

Copy前.jpg

这一步就可以看到已经准备调用_Block_copy函数了,在这之前打印Block发现是__NSStackBlock类型。接下来继续运行一步步跳入到_Block_copy实现,并在_Block_copy结束并准备返回(ret)前打断点,如下:


block源码.jpg

_Block_copy结束.png

这回可以看到Block已经变成了__NSMallocBlock。

_Block_copy源码解析

由上面的调试可知,Block的copy发生在_Block_copy函数中,接下来通过源码查看_Block_copy的流程:

void *_Block_copy(const void *arg) {
    struct Block_layout *aBlock;

    if (!arg) return NULL;
    
    // The following would be better done as a switch statement
    aBlock = (struct Block_layout *)arg;
    if (aBlock->flags & BLOCK_NEEDS_FREE) {
        // latches on high
        latching_incr_int(&aBlock->flags);
        return aBlock;
    }
    else if (aBlock->flags & BLOCK_IS_GLOBAL) {
        return aBlock; //如果是全局Block,则不需要copy,直接返回
    }
    else { // 如果是栈Block,则会申请一个堆空间创建一个新的Block,并将所有属性值拷贝一份
        // Its a stack block.  Make a copy.
        
        struct Block_layout *result =
            (struct Block_layout *)malloc(aBlock->descriptor->size);
        if (!result) return NULL;
        memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
#if __has_feature(ptrauth_calls)
        // Resign the invoke pointer as it uses address authentication.
        result->invoke = aBlock->invoke;
#endif
        // reset refcount
        result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING);    // XXX not needed
        result->flags |= BLOCK_NEEDS_FREE | 2;  // logical refcount 1
        _Block_call_copy_helper(result, aBlock);
        // Set isa last so memory analysis tools see a fully-initialized object.
        //最后把block的类型改为堆Block
        result->isa = _NSConcreteMallocBlock;
        return result;
    }
}

有源码可知,_Block_copy主要做了如下几件事情:

  • 1、判断如果是重复copy,则直接返回:
if (aBlock->flags & BLOCK_NEEDS_FREE) {
        // latches on high
        latching_incr_int(&aBlock->flags);
        return aBlock;
    }

当第一次copy之后BLOCK_NEEDS_FREE会被标记。下次在对同一个block进行copy时直接返回。

  • 2、判断如果是全局Block则直接返回:
if (aBlock->flags & BLOCK_IS_GLOBAL) {
        return aBlock; //如果是全局Block,则不需要copy,直接返回
    }

全局Block是不需要copy的。

  • 3、开辟堆空间,copy栈block到堆区
      struct Block_layout *result =
            (struct Block_layout *)malloc(aBlock->descriptor->size);
        if (!result) return NULL;
        memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
#if __has_feature(ptrauth_calls)
        // Resign the invoke pointer as it uses address authentication.
        result->invoke = aBlock->invoke;
#endif
        // reset refcount
        result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING);    // XXX not needed
        result->flags |= BLOCK_NEEDS_FREE | 2;  // logical refcount 1
        _Block_call_copy_helper(result, aBlock);
        // Set isa last so memory analysis tools see a fully-initialized object.
        //最后把block的类型改为堆Block
        result->isa = _NSConcreteMallocBlock;
        return result;

设置invoke:aBlock->invoke
更新Block的状态:flags
如果引用对象类型,copy对象:_Block_call_copy_helper(后面有专门对象copy的分析)
isa指针指向堆类型:result->isa = _NSConcreteMallocBlock

对象的Copy操作

对象的Copy是通过函数_Block_object_assign完成的。其源码如下:

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)
        _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;
    }
}
对象类型

对象类型有以下几种:

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.
};
普通对象copy

普通的对象(不加任何修饰比如__weak, 或者__block)的copy会被Block持有的同时还被Block进行retain使得引用计数增加:

        case BLOCK_FIELD_IS_OBJECT:
        /*******
        id object = ...;
        [^{ object; } copy];
        ********/
        // objc 指针地址 weakSelf (self)
            // arc
        _Block_retain_object(object);
            // 持有
        *dest = object;
引用的对象本身是个Block

Block也有可能捕获其他的block,对于Block类型对象,直接调用_Block_copy(前面已有分析):

case BLOCK_FIELD_IS_BLOCK:
        /*******
        void (^object)(void) = ...;
        [^{ object; } copy];
        ********/
            // block 被一个 block 捕获
        *dest = _Block_copy(object);
        break;
__block变量的Copy

__block对象跟普通对象不一样,在block里面它是一个结构体,它的copy实现是在函数_Block_byref_copy中实现的:

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
        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;
        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;
            }

            (*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出来的forwarding和原来的forwarding指向同一个,保证了block内部持有的变量和外部的__block变量是同一个:

copy->forwarding = copy; // patch heap copy to point to itself
src->forwarding = copy;  // patch stack to point to heap copy 

但是__block修饰的变量又可分为值变量类型(比如int类型等)和对象类型。对于对象类型,会调用对象的copy方法:

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;
            }

            (*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));
        }
其他对象的copy

其他对象没有copy,只是简单的指针赋值操作。

Block调用情况

Block是如何调用并执行里面的代码的呢?通过汇编断点继续调试:

block_invoke.jpg

block_invoke实现.jpeg

实际上是在底层调用了Block_layout的invoke实现的。

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

推荐阅读更多精彩内容