资讯专栏INFORMATION COLUMN

iOS:Block变量捕获

gclove / 1912人阅读

摘要:好的,我们定义的这个就是一个普通的局部变量,其实就是类型的局部变量语言基础。定义变量时加标识运行结果是。按照我们上面总结的这种类型的局部变量是要被捕获到内部的,但是应该不可以修改值。

这篇博客我们从一个很常见的题目入手。

int age = 10;   
void (^myblock)(void) =  ^{
  NSLog(@"%d",age);
};
age  = 20;
myblock();

这个题目就涉及到了block内访问外部变量,block有个变量捕获机制,

我们新建一个mac的命令行工程,把上面代码写进去,然后用clang把main.m文件编译为cpp的文件看一下。

具体的block底层结构上一篇文章我们已经说过了,这里我们针对结构就不在赘述,直接说核心点。

auto类型局部变量
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是什么情况。

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标识
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都是一样的。这里就不上代码细说了。

小结

局部变量:

会被block捕获到内部

auto类型的是值传递,内部不能修改值

static是指针传递,可以修改值。

全局变量:

不会被捕获到block内部

可以修改值


上面我们使用的是基本数据类型,那对象是不是也一样呢?

其实对象类型的也是一样的,但是有一个小点我们需要了解一下。

看下面这个例子吧。

NSMutableArray * arr = [NSMutableArray new];
   
void (^myblcok)(void) = ^{
  [arr addObject:@"1"];
  NSLog(@"%@",arr);
};
   
myblcok();

按照我们上面总结的这种auto类型的局部变量是要被捕获到内部的,但是应该不可以修改值。

上面代码执行后打印数组,是有@“1”这个元素的,这里我们就要搞清楚,我们调用[arr addObject:@"1"];并没有修改arr的值,只是只用了这个指针。

如果我们在block的回调中让arr=nil;,这算是改变arr的值,但是xcode就会报错的了。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/17258.html

相关文章

  • 探索iOSBlock的实现

    摘要:中结构体实例的初始化如下追加的成员变量由此可见,在结构体实例即中,自动变量值被捕获。可以将捕获自动变量总结为如下在执行语法时,中所使用的自动变量值被保存到的结构体实例即自身中。 Block作为Objective-C中闭包的实现在iOS开发中占有非常重要的地位,尤其是作为回调(callback)使用。这篇文章主要记录Block的实现,关于Block的语法可以参考这里:How Do I D...

    tyheist 评论0 收藏0
  • Block 到底啥时候会崩溃

    摘要:当函数返回时,函数的栈帧被销毁,这个的内存也会被清除。堆在栈中,说到过,当函数的栈帧的销毁,那么栈也会被随之清楚。 block可以说是OC一项非常好用的功能。block的本质,实际上是『带有自动变量值的匿名函数』。但是在block的使用上,有各种江湖传说,说在某某情况下,block的使用是不安全的,会造成崩溃。于是也有很多面试题喜欢考察block。但是,实际的block的不安全使用,貌...

    cppowboy 评论0 收藏0
  • Lua5.3学习笔记

    摘要:对于正常结束,将返回,并接上协程主函数的返回值。当错误发生时,将返回与错误消息。通过调用使协程暂停执行,让出执行权。通用形式的通过一个叫作迭代器的函数工作。 Lua 是一门强大、轻量的嵌入式脚本语言,可供任何需要的程序使用。Lua 没有 main 程序的概念: 它只能 嵌入 一个宿主程序中工作.宿主程序可以调用函数执行一小段 Lua 代码,可以读写 Lua 变量,可以注册 C 函数让 ...

    AWang 评论0 收藏0
  • Lua5.3学习笔记

    摘要:对于正常结束,将返回,并接上协程主函数的返回值。当错误发生时,将返回与错误消息。通过调用使协程暂停执行,让出执行权。通用形式的通过一个叫作迭代器的函数工作。 Lua 是一门强大、轻量的嵌入式脚本语言,可供任何需要的程序使用。Lua 没有 main 程序的概念: 它只能 嵌入 一个宿主程序中工作.宿主程序可以调用函数执行一小段 Lua 代码,可以读写 Lua 变量,可以注册 C 函数让 ...

    SimpleTriangle 评论0 收藏0
  • iOS 进阶必读 - 收藏集 - 掘金

    摘要:深入研究捕获外部变量和实现原理掘金前言是语言的扩充功能,而在和中引入了这个新功能。是由和两位大神在对的开发过程中中所有变换操作底层实现分析上掘金前言在上篇文章中,详细分析了是创建和订阅的详细过程。 深入研究Block捕获外部变量和__block实现原理 - 掘金 前言 Blocks是C语言的扩充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了这个新功能B...

    sf_wangchong 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<