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 copy
int j = __cself->j; // bound by copy
NSLog((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 ref
NSLog((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)
这两个方法有什么作用,什么时候用?
这两个问题下回分解。有问题欢迎来怼,怼怼更健康