Block截获自动变量实现与__block修饰符内部实现
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修饰符内部实现相关推荐
- Block 底层值__Block修饰符
__Block 修饰符 Block 想要改变外部的变量,必须要用__Block 来修饰自动变量. 根据内存地址可以看出来,__block 所修饰的变量,将外部的变量在栈中的内存地址放到了堆中. // ...
- java方法和变量修饰符有哪些_死磕Java基础---类,变量和方法的修饰符
欢迎关注微信公众号:一个自学的程序员 类修饰符 对于类的修饰符,毫无疑问是用来修饰类的,那么,修饰类的修饰符都有哪些? 有如下这些: 1. abstract 2. final 3. private 4 ...
- java修饰符默认_Java的类,方法,变量等等的默认修饰符是什么
访问控制修饰符 Java中,可以使用访问控制符来保护对类.变量.方法和构造方法的访问.Java支持4种不同的访问权限. 默认的,也称为default,在同一包内可见,不使用任何修饰符. 私有的,以pr ...
- java 中final修饰的变量_java中final修饰符的使用方法
本文为大家分享了java中final修饰符的使用,供大家参考,具体内容如下 1.final修饰符的用法: final可以修饰变量,被final修饰的变量被赋初始值之后,不能对它重新赋值. final可 ...
- Java反射之如何判断类或变量、方法的修饰符(Modifier解析)
a->public b->public static c->public static final d->private 就是返回这些 https://blog.csdn.ne ...
- java中的类、成员变量、方法的修饰符。
http://blog.sina.com.cn/s/blog_7ffb8dd501011alw.html http://www.cnblogs.com/lixiaolun/p/4311727.html ...
- 【Java】继承——成员变量的私有化(修饰符super进行访问)
package TcmStudy.day20;class Fu{// public int num01 = 3;public int num = 3;} class Zi extends Fu{// ...
- 【译文】 C#面向对象的基本概念 (Basic C# OOP Concept) 第一部分(类,对象,变量,方法,访问修饰符)...
译文出处:http://www.codeproject.com/Articles/838365/Basic-Csharp-OOP-Concept 相关文档:http://files.cnblogs.c ...
- iOS 与OS X多线程和内存管理 笔记 ARC与所有权修饰符
注:本文为笔记形式,所以很多都是摘抄的.<<iOS 与OS X多线程和内存管理>>书中写的很棒,简单易懂,建议各位看官自己去看看. ####ARC和MRC 前一篇主要是MRC环 ...
最新文章
- cache control 里 no-cache 和 no-store 的区别
- pandas to_csv参数详解_【Python基础】Pandas数据可视化原来也这么厉害
- 安装Windows 2003 域控制器
- 低秩矩阵补全算法matlab实现,推荐系统中的矩阵补全算法
- 【干货】PyTorch Tricks 集锦
- BZOJ2795/2890/3647 [Poi2012]A Horrible Poem 【字符串hash】
- iphone怎么录屏 苹果屏幕录制怎么操作
- 最近完成的APS生产排程工具,以甘特图展示排程结果
- 单目标跟踪(模板更新)(UpdateNet)《Learning the Model Update for Siamese Trackers》
- #后疫情时代的新思考#AI助力,“无接触”服务加速金融数字化转型丨数据猿公益策划...
- WhatsApp聊天记录迁移新手机,备份如何找回和删除?
- 对于rh v5系列服务器,华为rh2288v5服务器重定向问题引起pxe报错
- 三星nc10 装linux,三星NC10安装快捷键驱动
- Day 40 多表查询以及pymysql相关操作
- C语言程序设计 设计用函数实现模块化程序设计
- 前端学习之认识HTML
- 数据服务开发工具(Magic-API)
- 中兴冲破“阈值”,家庭监控市场或将重新排位
- C-PHY技术是什么
- vue教程——13 Vuex