Block实现原理

  • 1 新建一个工程在main函数中添加如下代码
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        int i = 2;
        void (^myBlock)() = ^ {

            printf("i的值是:%d\n",i);

        };
        i = 3;
        myBlock ();
    }
    return 0;
}

运行结果:i的值是:2 为什么是2 不是3呢?带着这个疑问我们往下看
编译main.m文件,1.打开终端,2.找到工程文件,3.找到main.m文件所在的位置,4.clang -rewrite-objc main.m重新编译会生成一个main.cpp的文件。这个文件相当大滑倒文件底部找到如下代码

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        int i = 2;
        void (*myBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, i));
        i = 3;
        ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
    }
    return 0;
}

由上面的代码我们看到Block被转化为了__main_block_impl_0的结构体实例

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int i;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=0) : i(_i) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

Desc :描述 block 的结构体变量;
i:参数
__main_block_impl_0 :结构体的构造函数,初始化结构体变量 impl、Desc;

impl:__block_impl 是 block 实现的结构体

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

isa:指向实例对象,说明 block 本身也是 Objective-C 对象;
Flags:按位承载 block 的附加信息;
Reserved :保留变量;
FuncPtr:函数指针,指向 Block 要执行的函数,即 __main_block_func_0;


static void __main_block_func_0
// __main_block_func_0 是 block 要最终要执行的函数代码
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int i = __cself->i; // bound by copy

            printf("i的值是:%d\n",i);

        }

__main_block_impl_0类型的参数__cself在__main_block_func_0内部copy了一份i,我们打印的实际上是新复制的这一份,所以block内部的i和外部的i不是同一份,不论外部i怎么变化,对block内部都没有影响,也就解释了为什么打印结果是2不是3

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

reserved :结构体信息保留字段
Block_size :结构体大小
此处已定义了一个该结构体类型的变量 __main_block_desc_0_DATA

  • 2 __block
    把main函数的代码改为如下
#import <Foundation/Foundation.h>

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

       __block int i = 2;

        void (^myBlock)() = ^ {

            printf("i的值是:%d\n",i);

        };
        i = 3;
        myBlock ();
    }

    return 0;
} 

运行结果:i的值是:3 这又是为什么呢?咱们继续往下看
clang -rewrite-objc main.m 打开main.cpp 找到如下代码

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

       __attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 2};

        void (*myBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344));
        (i.__forwarding->i) = 3;
        ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
    }

    return 0;
}

我们发现只是在参数i前面加__block,i的定义变复杂了很多,i 是__Block_byref_i_0类型的结构体

struct __Block_byref_i_0 {
  void *__isa;
__Block_byref_i_0 *__forwarding;
 int __flags;
 int __size;
 int i;
};

__isa :指向实例对象,表明i是OC的对象,不再是基本数据类型
__forwarding:指向自己的指针

 void (*myBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344));

main_block_impl_0 中引用的是 Block_byref_i_0 的结构体指针


struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_i_0 *i; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

从__main_block_impl_0结构体看到:i(_i->__forwarding),i的值是通过__forwarding指针取到的, (i.__forwarding->i) = 3;
修改的也是__forwarding指向的Block_byref_i_0结构体中的i,所以打印结果为3

在写的过程中参考了如下文章,谢谢文章作者
block的实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值