接《Block 截获自动变量实现与__block 修饰符内部实现》我们继续探讨 Block
留下的问题
; 这个指向自身的指针是什么鬼,有什么作用,什么时候用?
- __Block_byref_i_0 *__forwarding
这两个方法有什么作用,什么时候用?
- void (*copy) void (*dispose)
名称 | 实质 |
---|---|
Block | 栈上 Block 的结构体实例 |
__block 变量 | 栈上__block 变量的结构体实例 |
- struct __ViewController__viewDidLoad_block_impl_0 {struct __block_implimpl;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,intflags=0) : i(_i->__forwarding) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;
- Desc = desc;
- }
- };
我们都知道 isa 指针是指向其 class 的指针,所以,
也就表示这个 Block 为
- impl.isa = &_NSConcreteStackBlock;
类型。即栈上的 Block。
- _NSConcreteStackBlock
当然还有
类 | 设置对象的存储域 |
---|---|
_NSConcreteStackBlock | 栈 |
_NSConcreteGlobalBlock | 程序的数据区域(.data 区) |
_NSConcreteMallocBlock | 堆 |
根据名称,global(全局)我们创建一个全局的 Block,看一下他的编译结果
- #import"ViewController.h"
- //全局的Block
- void(^blk)(void) = ^{NSLog(@"123");};typedef void(^WxsBlock) ();@interface ViewController()
- @end
- @implementation ViewController- (void)viewDidLoad {
- [superviewDidLoad];
- blk();
- }@end
编译后
- struct __blk_block_impl_0 {
- struct __block_impl impl;
- struct __blk_block_desc_0 * Desc;
- __blk_block_impl_0(void * fp, struct __blk_block_desc_0 * desc, int flags = 0) {
- impl.isa = &_NSConcreteGlobalBlock;
- impl.Flags = flags;
- impl.FuncPtr = fp;
- Desc = desc;
- }
- };
- static void __blk_block_func_0(struct __blk_block_impl_0 * __cself) {
- NSLog((NSString * ) & __NSConstantStringImpl__var_folders_t9_g3xrsv653kz2gr7tmgwfbfvh0000gn_T_ViewController_fbc035_mi_0);
- }
- static struct __blk_block_desc_0 {
- size_t reserved;
- size_t Block_size;
- }
- __blk_block_desc_0_DATA = {
- 0,
- sizeof(struct __blk_block_impl_0)
- };
- static __blk_block_impl_0 __global_blk_block_impl_0((void * ) __blk_block_func_0, &__blk_block_desc_0_DATA);
- void( * blk)(void) = ((void( * )()) & __global_blk_block_impl_0)
我们发现
- impl.isa = &_NSConcreteGlobalBlock;
- @property(nonatomic,assign) blk_t blockAssgin;@property(nonatomic,strong) blk_t blockStrong;@property(nonatomic, copy) blk_t blockCopy;
- blk_t getBlk_t(inti) {
- blk_t block4 = ^(intcount){returncount*i;};NSLog(@"作为函数返回值,有自动变量截取---%@",block4);returnblock4;
- }
- {//作为普通变量, 有自动变量截取blk_t block1 = ^(intcount) {returncount * value;
- };NSLog(@"作为普通变量, 有自动变量截取---%@",block1);//作为assign类型成员变量 ,有自动变量截取
- self.blockAssgin= ^(intcount) {returncount * value;
- };NSLog(@"作为assign类型成员变量 ,有自动变量截取---%@",self.blockAssgin);//作为strong类型成员变量 ,有自动变量截取
- self.blockStrong= ^(intcount) {returncount * value;
- };NSLog(@"作为strong类型成员变量 ,有自动变量截取---%@",self.blockStrong);//作为copy类型成员变量 , 有自动变量截取
- self.blockCopy= ^(intcount) {returncount * value;
- };NSLog(@"作为copy类型成员变量 , 有自动变量截取---%@",self.blockCopy);//作为函数返回值,有自动变量截取getBlk_t(value);//作为函数参数,有自动变量截取[selfactionBlock:^int(inti) {returni*value;
- }];
- }
- - (void)actionBlock:(blk_t)block {//作为函数参数,无自动变量截取
- NSLog(@"作为函数参数,有自动变量截取---%@",block);
- block(1);
- }NSLog日志:
- Block[47786:5956467] 作为普通变量,有自动变量截取---<__NSMallocBlock__:0x60000024c9c0>
- Block[47786:5956467] 作为assign类型成员变量,有自动变量截取---<__NSStackBlock__:0x7fff5fbc39e0>
- Block[47786:5956467] 作为strong类型成员变量,有自动变量截取---<__NSMallocBlock__:0x608000056110>
- Block[47786:5956467] 作为copy类型成员变量,有自动变量截取---<__NSMallocBlock__:0x608000056620>
- Block[47786:5956467] 作为函数返回值,有自动变量截取---<__NSMallocBlock__:0x6080000565c0>
- Block[47786:5956467] 作为函数参数,有自动变量截取---<__NSStackBlock__:0x7fff5fbc3968>
但是通过 clang 编译出来的结果,他们全部同样全部都是
惊不惊喜?意不意外?
- impl.isa = &_NSConcreteStackBlock;
- {//作为返回值,无自动变量截取blk_t blockt = getBlk_t();NSLog(@"作为返回值,无自动变量截取blockt---%@",blockt);//作为普通变量,无自动变量截取,[email protected](strong,assgin,copy)这三种情况,是相同的结果
- self.blocktest= ^(intcount){returncount;};NSLog(@"作为普通变量,无自动变量截取----%@",self.blocktest);//作为函数参数,无自动变量截取[selfactionBlock:^int(inti) {NSLog(@"%d",i);returni;
- }];
- }
- - (void)actionBlock:(blk_t)block {//作为函数参数,无自动变量截取
- NSLog(@"作为函数参数,无自动变量截取---%@",block);
- block(1);
- }
- 打印结果
- Block[47535:5919554] 作为返回值,无自动变量截取blockt---<__NSGlobalBlock__:0x1039fd0f0>
- Block[47535:5919554] 作为普通变量,无自动变量截取----<__NSGlobalBlock__:0x1039fd150>
- Block[47535:5919554] 作为函数参数,无自动变量截取---<__NSGlobalBlock__:0x1039fd190>
- 全部是GlobalBlock
但是通过 clang 编译出来的结果,他们全部同样全部都是
惊不惊喜?意不意外?
- impl.isa = &_NSConcreteStackBlock;
通过 clang 的编译在 ARC 下只能看到静态情况下(静态代码声明)的 Block 类型,感觉只能作为参考,毕竟还有运行时。
Block 类型 | 有无自动变量截取 | 使用情况 |
---|---|---|
_NSConcreteGlobalBlock | 有 | 当作全局变量声明并初始化 |
无 | 全部情况 | |
NSStackBlock | 有 | assgin 类型的成员变量 |
作为函数参数 | ||
无 | 无 | |
_NSConcreteMallocBlock | 有 | 作为普通的局部变量 |
作为 copy 类型的成员变量 | ||
作为 strong 类型的成员变量 | ||
作为函数返回值 | ||
无 | 无 |
因为在使用全局变量的地方不会截获自动变量。因为它在一开始就已经定义好了。不存在 "变量",所以它的执行在任何时候都是固定的,不会依赖运行时的环境,这种类型的操作全局只有一个就可以,既节省空间又好管理。所以放在全局静态区的数据区域最好不过了。
通过 clang 编译的结果可知,除了在全局变量声明并创建的时候 block 被编译成
其他时候全部都是
- _NSConcreteGlobalBlock
类型,我们不难看出,Block 的初始状态只有这两种,所以 Malloc 状态的 Block 是从 Stack 状态转变过去的,这个猜想在看《Objective-C 高级编程》的时候也和作者不谋而合。
- _NSConcreteMallocBlock
那么我们就来探讨一下它是如何从栈被搞到堆中去的。
根据《Objective-C 高级编程》的描述: 它是通过
方法来把 Block 从栈搞到堆中的, 通过 objc4 运行库的 runtime/objc-arr.mm 可知:
- objc_retainBlock()
=
- objc_retainBlock()
- _Block_copy
- blk_t func(int rate) {
- /*
- 通过Block语法生成Block,配置在栈上的结构体实例,
- tmp被赋值
- */
- blk_t tmp = &__func_block_impl_0(...);
- /*
- 通过_Block_copy将其复制到堆上
- 复制后将堆上的地址作为指针复制给变量tmp
- */
- tmp = _Block_copy(tmp);
- /*
- 将堆上的Block作为Objective—C对象
- 注册到autoreleasepool中,返回该对象
- */returnobjc_autoreleaseReturnValue(tmp);
- }
当作为返回值的时候,编译器显然是可以搞定的
但是再作为函数参数的时候就不太好使了,这个时候需要我们手动的去 copy Block,这一点通过 "作为函数参数,有自动变量截取" 验证实例得到了验证,它都是 stack 类型的
俩字:安全! 复制到堆上的__block 变量和 Block 变量在变量作用域结束时不受影响
向方法或者函数的参数重传递 Block 时
但是如果在方法或者函数中适当的复制了传递来的参数,那么就不必在调用该方法或者函数前手动复制了,如一下方法:
1,Cocoa 框架的方法且方法名字中有 usingBlock 等时 2,GCD 的 API
来源: http://www.bubuko.com/infodetail-2036345.html