前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >block内部实现

block内部实现

作者头像
老沙
发布2019-09-27 19:30:23
3840
发布2019-09-27 19:30:23
举报
文章被收录于专栏:老沙课堂老沙课堂

一. block实质

1. block本质
1>.举个例子
代码语言:javascript
复制
int main(int argc, const char * argv[]) {    @autoreleasepool {        // insert code here...        int c = 10;        static int d = 10;        void (^testBlock)(int a, NSString *b) = ^(int a, NSString * b) {            NSLog(@"c = %d, d = %d", c, d);        };        d = 20;        testBlock(30,@"40");    }    return 0;}

输出:

代码语言:javascript
复制
Block内部实现原理[96327:13489327] c = 10, d = 20
2>. 内部实现原理

a. 我们将block 转成cpp文件

代码语言:javascript
复制
int main(int argc, const char * argv[]) {    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;        int c = 10;        static int d = 10;        void (*testBlock)(int a, NSString *b) = ((void (*)(int, NSString *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, c, &d));        d = 20;        ((void (*)(__block_impl *, int, NSString *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock, 30, (NSString *)&__NSConstantStringImpl__var_folders__9_b_xp2qr12j348_dq41f7zqqh0000gn_T_main_d0fe91_mi_1);    }    return 0;}

b. 看着是有点乱 但是把类型强制转换去掉我们在看一下

代码语言:javascript
复制
  int main(int argc, const char * argv[]) {        int c = 10;        static int d = 10;        // 定义block  生成__main_block_impl_0结构体 并传入两个参数        // 1.block方法        // 2.blcok信息        void (*testBlock)(int a, NSString *b) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, c, &d));            d = 20;            // testBLock->FuncPtr   参数为testblock 30 和字符串        ((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock, 30, (NSString *)&__NSConstantStringImpl__var_folders__9_b_xp2qr12j348_dq41f7zqqh0000gn_T_main_d0fe91_mi_1);    }    return 0;}

c. 我们先看一下__ main_block_impl_0结构体和 __ main_block_func_0 还有__main_block_desc_0_DATA都代表写什么

代码语言:javascript
复制
struct __main_block_impl_0 {  struct __block_impl impl;  struct __main_block_desc_0* Desc;    // c 和 d 是block捕捉(capture)进来的。如果block没有引用,则不会出现c和d  // 具体什么情况会捕捉,文章后面会讲解。同学可以去验证下  // 没有修饰的为值传递  int c;  // static 修饰的为地址传递  int *d;  // 构造函数    c(_c), d(_d) 直接_d 给d赋值 直接_c 给c赋值  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _c, int *_d, int flags=0) : c(_c), d(_d) {    impl.isa = &_NSConcreteStackBlock;    impl.Flags = flags;    impl.FuncPtr = fp;    Desc = desc;  }};

d. 通过这个 我们可以知道 d为地址传递 c为值传递。所以当d重新赋值后, 因为d传递的是d变量的地址。所以值会跟着改变,而c为值传递 所以当c改变的时候 block结构体中并不会跟着改变。

我们继续往下看传入的参数main_block_func_0和__main_block_desc_0_DATA

  • main_block_func_0
代码语言:javascript
复制
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int a, NSString *b) {  // _cself 为结构体本身  获取c 和 *d  int c = __cself->c; // bound by copy  int *d = __cself->d; // bound by copy  NSLog((NSString *)&__NSConstantStringImpl__var_folders__9_b_xp2qr12j348_dq41f7zqqh0000gn_T_main_d0fe91_mi_0, c,(*d));}
  • __main_block_desc_0_DATA
代码语言:javascript
复制
static struct __main_block_desc_0 {  size_t reserved;  //__main_block_impl_0的大小  size_t Block_size;} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

e. 我们继续往下看 我们如何调用block 我们简化一下代码

代码语言:javascript
复制
(__block_impl *)testBlock)->FuncPtr((__block_impl *)testBlock, 30, (NSString *)&__NSConstantStringImpl__var_folders__9_b_xp2qr12j348_dq41f7zqqh0000gn_T_main_d0fe91_mi_1);    

通过testBlock强制转换为__block_impl * 去调用FuncPtr 然后传入参数testBlock,30和字符串

我们可以看到定义的时候 testBlock 为&__main_block_impl_0

从下图构造函数 我们可以知道 传入的block方法给了impl的funcptr。funcptr也就是上面的main_block_func_0

代码语言:javascript
复制
struct __main_block_impl_0 {  struct __block_impl impl;  struct __main_block_desc_0* Desc;  // 构造函数    c(_c), d(_d) 直接_d 给d赋值 直接_c 给c赋值  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _c, int *_d, int flags=0) : c(_c), d(_d) {    impl.isa = &_NSConcreteStackBlock;    impl.Flags = flags;    impl.FuncPtr = fp;    Desc = desc;  }};

:为什么testBlock可以直接调用impl中的FuncPtr呢 ?

答: 因为impl为__ main_block_impl_0中的第一个结构体.在内存中是头指针是指向的一个地址 所以可以转换。

3>. 结论

通过上述的源码 我们可以知道了结构体的大致结构。以及调用方式 下图看起来可能更直观

  • block构造
  • block调用
2. block capture

变量类型

是否可以被捕捉

访问方式

auto(默认为auto) int a == auto int a

值传递

static

地址传递

全局变量

不需要捕获

直接访问

二. block 类型

block分为以下三个类型
  • __ NSGlobalBlock __
  • __ NSStackBlock __
  • __ NSMallocBlock __
继承关系

__ NSMallocBlock __ , __ NSMallocBlock , NSBlock, NSObject

__ NSStackBlock __ , __ NSStackBlock , NSBlock, NSObject

__ NSGlobalBlock __ , __ NSGlobalBlock __ , NSBlock, NSObject

如何分配

__ NSGlobalBlock __

没有访问auto对象

__ NSStackBlock __

访问了auto对象

__ NSMallocBlock __

__ NSStacBlock __ 调用了copy

关闭arc验证

在ARC上

__ NSStackBlock __ 在进行copy 会copy到 __ NSMallocBlock __区

目的是为了防止block自动释放

如有错误请指。相互交流学习。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老沙说点事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一. block实质
    • 1. block本质
      • 1>.举个例子
        • 2>. 内部实现原理
          • 3>. 结论
            • 2. block capture
            • 二. block 类型
              • block分为以下三个类型
                • 继承关系
                  • 如何分配
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档