Block自动截获变量

在Block中访问一个外部的局部变量,Block会持用它的临时状态,外部局部变量的变化不会影响它的的状态。

eg:

typedef void(^WxsBlock) ();- (void)viewDidLoad {[super viewDidLoad];int i = 0;int j = 2;WxsBlock completeBlock = ^(){NSLog(@"in block : %d",i);NSLog(@"in block : %d",j);};i++;j++;NSLog(@"out block : %d",i);NSLog(@"out block : %d",j);completeBlock();}

我们通过clang编译成cpp文件来看它做了什么
如果对clang的代码不熟悉,先看Block实现

关键代码:


typedef void(*WxsBlock) ();struct __ViewController__viewDidLoad_block_impl_0 {struct __block_impl impl;struct __ViewController__viewDidLoad_block_desc_0* Desc;int i;int j;__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, int _i, int _j, int flags=0) : i(_i), j(_j) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {int i = __cself->i; // bound by copyint j = __cself->j; // bound by copyNSLog((NSString *)&__NSConstantStringImpl__var_folders_t9_g3xrsv653kz2gr7tmgwfbfvh0000gn_T_ViewController_5c4327_mi_0,i);NSLog((NSString *)&__NSConstantStringImpl__var_folders_t9_g3xrsv653kz2gr7tmgwfbfvh0000gn_T_ViewController_5c4327_mi_1,j);}static struct __ViewController__viewDidLoad_block_desc_0 {size_t reserved;size_t Block_size;
} __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0)};static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));int i = 0;int j = 2;WxsBlock completeBlock = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, i, j));i++;j++;NSLog((NSString *)&__NSConstantStringImpl__var_folders_t9_g3xrsv653kz2gr7tmgwfbfvh0000gn_T_ViewController_5c4327_mi_2,i);NSLog((NSString *)&__NSConstantStringImpl__var_folders_t9_g3xrsv653kz2gr7tmgwfbfvh0000gn_T_ViewController_5c4327_mi_3,j);((void (*)(__block_impl *))((__block_impl *)completeBlock)->FuncPtr)((__block_impl *)completeBlock);}

对比Block实现 中的解析来看,我们发现
__ViewController__viewDidLoad_block_impl_0 结构体种多了

    int i;int j;

两个变量,并且在其构造方法中多了(int _i, int _j,)的传参和: i(_i), j(_j)的赋值。

在继续深入,我们看到i 和 j__ViewController__viewDidLoad_block_func_0结构题中通过__ViewController__viewDidLoad_block_impl_0的对象指针创造了一个新的局部变量,也叫i``j

看到这里,我们应该明白了:
1,在block创建的时候,如果block中使用了局部变量,block会在爱其__ViewController__viewDidLoad_block_impl_0结构体中复制一个同样类型,同样值的变量。
2,block的执行动作是一个方法指针__ViewController__viewDidLoad_block_func_0,在执行这个方法时,block讲表示自身的__ViewController__viewDidLoad_block_impl_0 *__cself当作参数传递进去,间接的获得了其中的i j的值,并使用。

所以block的接获自动变量这样看起来也并没有什么,就是简单的复制值而已。

但是我们可能会产生一个疑问,为什么要截获?

这个就得来考虑应用场景了

scene:

一个WxsTool工具类,有一个检测数据的功能,检测完毕后通过自身持有的block返回结果,如图:

如果block在创建时没有捕获变量model,而是获取它的指针。
在执行检测处理的时候,model在其他的地方改变了,那么检测过程会调用block通知结果,这时获取的结果很有可能和预想的不一样,而你也不好找到原因。

总的来说,block的使用和创建是分开的,在其之间的操作并不能保证对局部变量值的变动性有掌控。所以,干脆直接补货,在创建那一个时刻是什么,就是什么,这样就避免了在使用过程冲被更改的问题。

有点数据同步的味道在里边。

好了 ,问题总是一个接一个,又会有人问,我就是想让这个model在处理时也可以被改变怎么办?

其实这个问题还和另外一个问题关联:

在block中为什么不能更改局部变量的值?

__block 我们走!!!

__block 修饰符终于登场了

我们写一个OC 使用__block修饰符的代码:

typedef void(^WxsBlock) ();- (void)viewDidLoad {[super viewDidLoad];__block int i = 0;WxsBlock completeBlock = ^(){NSLog(@"before change:%d",i);i = 6;NSLog(@"after change:%d",i);};completeBlock();}

clang编译出来的代码:


struct __Block_byref_i_0 {void *__isa;
__Block_byref_i_0 *__forwarding;int __flags;int __size;int i;
};struct __ViewController__viewDidLoad_block_impl_0 {struct __block_impl impl;struct __ViewController__viewDidLoad_block_desc_0* Desc;__Block_byref_i_0 *i; // by ref__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_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;}
};static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {__Block_byref_i_0 *i = __cself->i; // bound by refNSLog((NSString*)&__NSConstantStringImpl__var_folders_t9_g3xrsv653kz2gr7tmgwfbfvh0000gn_T_ViewController_c50572_mi_0,(i->__forwarding->i));(i->__forwarding->i) = 6;NSLog((NSString*)&__NSConstantStringImpl__var_folders_t9_g3xrsv653kz2gr7tmgwfbfvh0000gn_T_ViewController_c50572_mi_1,(i->__forwarding->i));
}static void __ViewController__viewDidLoad_block_copy_0(struct __ViewController__viewDidLoad_block_impl_0*dst, struct __ViewController__viewDidLoad_block_impl_0*src) {_Block_object_assign((void*)&dst->i, (void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);
}static void __ViewController__viewDidLoad_block_dispose_0(struct __ViewController__viewDidLoad_block_impl_0*src) {_Block_object_dispose((void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);
}static struct __ViewController__viewDidLoad_block_desc_0 {size_t reserved;size_t Block_size;void (*copy)(struct __ViewController__viewDidLoad_block_impl_0*, struct __ViewController__viewDidLoad_block_impl_0*);void (*dispose)(struct __ViewController__viewDidLoad_block_impl_0*);
} __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0), __ViewController__viewDidLoad_block_copy_0, __ViewController__viewDidLoad_block_dispose_0
};static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));__attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 0};WxsBlock completeBlock = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344));((void (*)(__block_impl *))((__block_impl *)completeBlock)->FuncPtr)((__block_impl *)completeBlock);}

我们发现,相较于不添加__block修饰符,多出了一个

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

结构体,它的作用是把被__block修饰符修饰的变量转换成struct结构体

我们进一步看它的应用。

1,在__ViewController__viewDidLoad_block_impl_0结构体中多了__Block_byref_i_0 *i而不是之前的int i;,其中的__forwarding指针是一个指向自身的指针。这个指针的作用牵扯到了block的作用域,我们这次不说,敬请听下回分解

2,在__ViewController__viewDidLoad_block_impl_0结构体中的构造方法中传入了__Block_byref_i_0 *i并且有一个i(_i->__forwarding)的操作,相比较值钱的i(_i)可知,这里将_i->__forwarding赋值给了impl_0结构体中的i.其实还是指向自己(i)。

__ViewController__viewDidLoad_block_desc_0结构体中也发生了很大的变化

原来的desc_0结构体只有一个表示大小的size 一个冗余的reserved
现在多了

  void (*copy)(struct __ViewController__viewDidLoad_block_impl_0*, struct __ViewController__viewDidLoad_block_impl_0*);
  void (*dispose)(struct __ViewController__viewDidLoad_block_impl_0*);

两个函数

这两个函数我们暂且解释为一个copy函数,一个释放函数,暂且和 MRC中retain release的作用,我们后边在说Block存储域的时候这两个函数的作用会不言而喻的。

综述

我们最应该关注的点在于,被__block修饰的变量转换成了

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

结构体,

在block 的struct中 持有了此结构体的指针,而不是变量的值,这样就可以通过它来访问内存,所以,可以对值进行修改。

知道这个的同事我们又遇到了几个问题:

1,__Block_byref_i_0 *__forwarding;这个指向自身的指针是什么鬼,有什么作用,什么时候用?
2,Desc_0结构体中多出来的void (*copy) void (*dispose)这两个方法有什么作用,什么时候用?

这两个问题下回分解。有问题欢迎来怼,怼怼更健康

Block截获自动变量实现与__block修饰符内部实现相关推荐

  1. Block 底层值__Block修饰符

    __Block 修饰符 Block 想要改变外部的变量,必须要用__Block 来修饰自动变量. 根据内存地址可以看出来,__block 所修饰的变量,将外部的变量在栈中的内存地址放到了堆中. // ...

  2. java方法和变量修饰符有哪些_死磕Java基础---类,变量和方法的修饰符

    欢迎关注微信公众号:一个自学的程序员 类修饰符 对于类的修饰符,毫无疑问是用来修饰类的,那么,修饰类的修饰符都有哪些? 有如下这些: 1. abstract 2. final 3. private 4 ...

  3. java修饰符默认_Java的类,方法,变量等等的默认修饰符是什么

    访问控制修饰符 Java中,可以使用访问控制符来保护对类.变量.方法和构造方法的访问.Java支持4种不同的访问权限. 默认的,也称为default,在同一包内可见,不使用任何修饰符. 私有的,以pr ...

  4. java 中final修饰的变量_java中final修饰符的使用方法

    本文为大家分享了java中final修饰符的使用,供大家参考,具体内容如下 1.final修饰符的用法: final可以修饰变量,被final修饰的变量被赋初始值之后,不能对它重新赋值. final可 ...

  5. Java反射之如何判断类或变量、方法的修饰符(Modifier解析)

    a->public b->public static c->public static final d->private 就是返回这些 https://blog.csdn.ne ...

  6. java中的类、成员变量、方法的修饰符。

    http://blog.sina.com.cn/s/blog_7ffb8dd501011alw.html http://www.cnblogs.com/lixiaolun/p/4311727.html ...

  7. 【Java】继承——成员变量的私有化(修饰符super进行访问)

    package TcmStudy.day20;class Fu{// public int num01 = 3;public int num = 3;} class Zi extends Fu{// ...

  8. 【译文】 C#面向对象的基本概念 (Basic C# OOP Concept) 第一部分(类,对象,变量,方法,访问修饰符)...

    译文出处:http://www.codeproject.com/Articles/838365/Basic-Csharp-OOP-Concept 相关文档:http://files.cnblogs.c ...

  9. iOS 与OS X多线程和内存管理 笔记 ARC与所有权修饰符

    注:本文为笔记形式,所以很多都是摘抄的.<<iOS 与OS X多线程和内存管理>>书中写的很棒,简单易懂,建议各位看官自己去看看. ####ARC和MRC 前一篇主要是MRC环 ...

最新文章

  1. cache control 里 no-cache 和 no-store 的区别
  2. pandas to_csv参数详解_【Python基础】Pandas数据可视化原来也这么厉害
  3. 安装Windows 2003 域控制器
  4. 低秩矩阵补全算法matlab实现,推荐系统中的矩阵补全算法
  5. 【干货】PyTorch Tricks 集锦
  6. BZOJ2795/2890/3647 [Poi2012]A Horrible Poem 【字符串hash】
  7. iphone怎么录屏 苹果屏幕录制怎么操作
  8. 最近完成的APS生产排程工具,以甘特图展示排程结果
  9. 单目标跟踪(模板更新)(UpdateNet)《Learning the Model Update for Siamese Trackers》
  10. #后疫情时代的新思考#AI助力,“无接触”服务加速金融数字化转型丨数据猿公益策划...
  11. WhatsApp聊天记录迁移新手机,备份如何找回和删除?
  12. 对于rh v5系列服务器,华为rh2288v5服务器重定向问题引起pxe报错
  13. 三星nc10 装linux,三星NC10安装快捷键驱动
  14. Day 40 多表查询以及pymysql相关操作
  15. C语言程序设计 设计用函数实现模块化程序设计
  16. 前端学习之认识HTML
  17. 数据服务开发工具(Magic-API)
  18. 中兴冲破“阈值”,家庭监控市场或将重新排位
  19. C-PHY技术是什么
  20. vue教程——13 Vuex

热门文章

  1. 特征选择:Boston house prices 数据集分析(R 语言)
  2. MySQL数据导入导出的两种方式
  3. 智创云享小程序源码下载+无限裂变
  4. Java 10W字面经
  5. [附源码]Python计算机毕业设计SSM基于的优质房源房租管理系统(程序+LW)
  6. renpy-快速入门
  7. gdal处理tiff文件的小问题
  8. java 百度鹰眼sdk,百度地图API基础操作--百度鹰眼
  9. display:flex如何设置内容水平垂直居中
  10. 关联规则挖掘(南京大学复杂数据结构挖掘课程作业)