本文主要介绍__block变量、block copy对外部引用变量的影响。
©原创文章,转载请注明出处!
__block storage-class-specifier
我们知道在block内可以引用block外部的变量,那么能随意修改block外部的变量吗?
答案显然是否定的,不然就没有下文了^_^
在block内部能修改的变量有:
- 全局变量、全局静态变量、静态变量以及类的成员变量;
- 用
__block
修饰的自变量。
本文的主角就是__block
,更准确的说是__block storage-class-specifier
(C语言中storage-class-specifier
有:typedef
、extern
、static
、auto
以及register
),其主要用于修饰需要在block中修改的自变量(automatic variables)。
那么,编译器是如何处理__block
变量的呢?
是不是有点复杂…
__block类型的变量会被编译器转换为一个结构体。
在最新的开源代码中,用于表示__block
变量的结构Block_byref
定义如下:
1 |
|
Block_byref
与Block_descriptor
类似,也是就3部分组成。
由codeA可以看到Block_byref
也含有isa
指针,因此也可以将其看作OC对象,但从编译器转换结果来看,该isa
指针被置为nil
。
通过前面的分析可以得出如下结论:
block以及__block修饰的变量本质上都是结构体类型的自变量(an automatic variable of a struct)。(来源)
block copy与其引用的外部变量
block copy与普通object类型变量
前文我们总结过,当block从stack copy到heap上时,会retain其引用的外部object。
我们看看编译器为此做了什么:
1 | int main() |
block在copy时,会调用其copy
方法完成对引用变量的copy,编译器为blockA
合成的copy
函数如下:
1 | static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) |
可以看到,合成的copy方法__main_block_copy_0
调用了runtime的_Block_object_assign
方法来完成copy操作:
1 | void _Block_object_assign(void *destAddr, const void *object, const int flags) |
第24行,retain了block内引用的对象,在本例中为arr
。
第25行,将源block中arr
的地址赋给了目标block中的arr
变量。
block copy与__block修饰的object类型变量
我们知道,在ARC下block会retain __block修饰变量,而在非ARC下不会retain。
还是通过代码来了解:
1 | int main() |
编译器合成的代码:
1 | int main() |
为了讨论方便我们将blockArr
称为__block变量,arr
称为原始__block变量。
前面我们讲到在ARC下block会retain __block修饰变量,而在非ARC下不会retain,严格地讲这里所说的变量是指原始__block变量。
之所以ARC下会retain原始__block变量,是因为ARC下变量默认声明为__strong
(上面代码的第37行:NSArray *blockArr;
)。
通过比较可知:编译器为blockB
合成的copy函数与为blockA
合成的copy函数唯一的差别在调用_Block_object_assign
函数时最后一个参数不一样。
1 |
|
1 | static void _Block_byref_assign_copy(void *dest, const void *arg, const int flags) |
对blockB
的copy操作会执行到codeH中的第27行,表示执行__block变量自己的copy函数,即codeF中的__Block_byref_id_object_copy_131
函数。
呵呵,最终又回到了_Block_object_assign
函数,终极执行的代码是codeD中的第11行:_Block_assign((void *)object, destAddr);
,
很明显,只是指针间的赋值,没有任何retain
操作,也就说明了在非ARC下,block不会retain
原始__block变量。
_Block_object_assign
函数最后一个参数flags
的含义:
1 | enum |
有点晕了吧,看几张图吧,清醒一下^_^
注意:下面讲的都是block变量本身,而非原始block变量
block从stack copy到heap上时对__block变量的影响(来源):
当block从stack copy到heap上时,对于在其内部使用的、并且在stack上的__block变量也会被copy到heap上,并且会持有该变量(来源):
当该__block变量已经被copy到heap上时,其引用计数会+1(来源):
当heap上的block被释放时,其内部引用到的__block变量也会被release(来源):