这篇博客咱们从一个很常见的题目入手。c++
int age = 10; void (^myblock)(void) = ^{ NSLog(@"%d",age); }; age = 20; myblock();
这个题目就涉及到了block内访问外部变量,block有个变量捕获机制,数组
咱们新建一个mac的命令行工程,把上面代码写进去,而后用clang把main.m文件编译为cpp的文件看一下。xcode
具体的block底层结构上一篇文章咱们已经说过了,这里咱们针对结构就不在赘述,直接说核心点。函数
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int age; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
能够看到,在block的内部,多了一个age变量。并且从初始化的函数来看,这个__main_block_impl_0
中age的值是外面传进来赋给他的。咱们看一下main函数中。命令行
int age = 10; void (*myblock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age)); age = 20; ((void (*)(__block_impl *))((__block_impl *)myblock)->FuncPtr)((__block_impl *)myblock);
能够看出来在这个block初始化的过程当中,就把age的值,也就是10,传了进去,赋值给了block内部的age变量。指针
那我再看一下打印的时候的age是什么状况。code
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int age = __cself->age; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_6l_80sfw0tn35bg4dxc51jlq_bw0000gn_T_main_3b5bf4_mi_0,age); }
打印的这个age就是block本身内部的这个ege变量(值为10),因此咱们在执行block以前,改变外面的age值为20,其实改变的不是内部要打印的这个age了,因此打印出来结果仍是10。对象
好的,咱们定义的这个age就是一个普通的局部变量,其实就是auto类型的局部变量(C语言基础)。那咱们下面改变一下这个变量的属性,尝试一下静态变量(static)和全局变量。内存
首先是静态变量。博客
//定义变量时加static标识 static int age = 10;
运行结果是20。咱们依然要看一下clang编译一下,看c++代码。
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int *age; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_age, int flags=0) : age(_age) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
咱们来分析一下两次的异同,首先block都对变量进行了捕获,但不一样的是static类型的变量是捕获了变量的指针,那咱们应该就能够理解了,通常状况下这种基本数据类型若是是传指针,就意味要修改值的。
咱们从main函数中也能够看出来,在初始化block的时候传入了外部age的指针,
static int age = 10; void (*myblock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &age));
并且在打印的时候也是打印了这个指针指向的数据。
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int *age = __cself->age; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_6l_80sfw0tn35bg4dxc51jlq_bw0000gn_T_main_d618bf_mi_0,(*age)); }
因此咱们在执行block以前更改了age的值,在执行block的时候打印的也是同一块内存的值,因此值改变了。
最后咱们在试一下全局变量。
在定义age的时候,定义成一个全局变量。(拿到main方法外面)。
int age = 10; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
从编译后的代码能够看出,在底层其实也是生成了一个age=10的全局变量,而后在block内部并无捕获这个变量。
在无论是从新赋值,仍是输出打印,都是操做的这个全局的age,因此这个值也是能被改变的。
可能有人会问若是是
static int age = 10;
这个样子的全局变量呢?
全局变量加不加static都是同样的。这里就不上代码细说了。
小结
局部变量:
全局变量:
上面咱们使用的是基本数据类型,那对象是否是也同样呢?
其实对象类型的也是同样的,可是有一个小点咱们须要了解一下。
看下面这个例子吧。
NSMutableArray * arr = [NSMutableArray new]; void (^myblcok)(void) = ^{ [arr addObject:@"1"]; NSLog(@"%@",arr); }; myblcok();
按照咱们上面总结的这种auto类型的局部变量是要被捕获到内部的,可是应该不能够修改值。
上面代码执行后打印数组,是有@“1”这个元素的,这里咱们就要搞清楚,咱们调用[arr addObject:@"1"];
并无修改arr的值,只是只用了这个指针。
若是咱们在block的回调中让arr=nil;
,这算是改变arr的值,可是xcode就会报错的了。