iOS开发:block的探究(2)

来源:互联网 发布:qq for mac os x 10.7 编辑:程序博客网 时间:2024/06/08 10:56

[4. Capture local variable]

再看一个访问局部变量的block是怎样的。

生成中间代码,得到片段如下:

可以看出这次的block结构体__main_block_impl_0多了个成员变量i,用来存储使用到的局部变量i(值为1024);并且此时可以看到__cself参数的作用,类似C++中的this和Objective-C的self。

如果我们尝试修改局部变量i,则会得到如下错误:

错误信息很详细,既告诉我们变量不可赋值,也提醒我们要使用__block类型标识符。

为什么不能给变量i赋值呢?

因为main函数中的局部变量i和函数__main_block_func_0不在同一个作用域中,调用过程中只是进行了值传递。当然,在上面代码中,我们 可以通过指针来实现局部变量的修改。不过这是由于在调用__main_block_func_0时,main函数栈还没展开完成,变量i还在栈中。但是在 很多情况下,block是作为参数传递以供后续回调执行的。通常在这些情况下,block被执行时,定义时所在的函数栈已经被展开,局部变量已经不在栈中 了(block此时在哪里?),再用指针访问就……。

所以,对于auto类型的局部变量,不允许block进行修改是合理的。


[5. Modify static local variable]

于是我们也可以推断出,静态局部变量是如何在block执行体中被修改的——通过指针。

因为静态局部变量存在于数据段中,不存在栈展开后非法访存的风险。

上面中间代码片段与前一个片段的差别主要在于main函数里传递的是i的地址(&i),以及__main_block_impl_0结构体中成员i变成指针类型(int *)。

然后在执行block时,通过指针修改值。

当然,全局变量、静态全局变量都可以在block执行体内被修改。更准确地讲,block可以修改它被调用(这里是__main_block_func_0)时所处作用域内的变量。比如一个block作为成员变量时,它也可以访问同一个对象里的其它成员变量。


[6. Implementation of __block variable]

那么,__block类型变量是如何支持修改的呢?

 

我们为int类型变量加上__block指示符,使得变量i可以在block函数体中被修改。

此时再看中间代码,会多出很多信息。首先是__block变量对应的结构体:

由第一个成员__isa指针也可以知道__Block_byref_i_0也可以是NSObject。

第二个成员__forwarding指向自己,为什么要指向自己?指向自己是没有意义的,只能说有时候需要指向另一个__Block_byref_i_0结构。

最后一个成员是目标存储变量i。

此时,__main_block_impl_0结构如下:

__main_block_impl_0的成员变量i变成了__Block_byref_i_0 *类型。

对应的函数__main_block_func_0如下:

亮点是__Block_byref_i_0指针类型变量i,通过其成员变量__forwarding指针来操作另一个成员变量。 :-)

而main函数如下:

通过这样看起来有点复杂的改变,我们可以修改变量i的值。但是问题同样存在:__Block_byref_i_0类型变量i仍然处于栈上,当block被回调执行时,变量i所在的栈已经被展开,怎么办?

在这种关键时刻,__main_block_desc_0站出来了:

此时,__main_block_desc_0多了两个成员函数:copy和dispose,分别指向__main_block_copy_0和__main_block_dispose_0。

当block从栈上被copy到堆上时,会调用__main_block_copy_0将__block类型的成员变量i从栈上复制到堆上;而当block被释放时,相应地会调用__main_block_dispose_0来释放__block类型的成员变量i。

一会在栈上,一会在堆上,那如果栈上和堆上同时对该变量进行操作,怎么办?

这时候,__forwarding的作用就体现出来了:当一个__block变量从栈上被复制到堆上时,栈上的那个__Block_byref_i_0结构体中的__forwarding指针也会指向堆上的结构。


本来还想继续写下去,结果发现文章有点长了。先到此。

0 0
原创粉丝点击