Block技术合集
Block的写法及使用
iOS - __block 修饰符底层探索
先看代码
static int number1 = 10; //全局静态变量
// Block捕获变量
- (void)captureVariableBlock {
static int number2 = 10; //局部静态变量
int number3 = 10; //局部变量
__block int number4 = 10; //__block修饰的局部变量
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"obj1",@"obj2", nil];
void(^captureBlock)(void) = ^ {
NSLog(@"capture Variable \n number1 = %d, \n number2 = %d, \n number3 = %d, \n number4 = %d, \n array = %@", number1, number2, number3, number4, array);
[array addObject:@"obj4"];
};
number1 = 2;
number2 = 2;
number3 = 2;
number4 = 2;
[array addObject:@"obj3"];
array = nil;
captureBlock();
}
}
打印结果:
2021-05-01 17:39:26.836630+0800 BlockDemo[14620:6891187] capture Variable
number1 = 2,
number2 = 2,
number3 = 10,
number4 = 2,
array = (
obj1,
obj2,
obj3
)
通过终端命令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc ViewController.m -o ViewController.cpp
获取编译后的.cpp文件,经整理简化后如下:
struct __ViewController__captureVariableBlock_block_impl_0 {
struct __block_impl impl;
struct __ViewController__captureVariableBlock_block_desc_0* Desc;
int *number2;
int number3;
NSMutableArray *array;
__Block_byref_number4_2 *number4; // by ref
__ViewController__captureVariableBlock_block_impl_0(void *fp, struct __ViewController__captureVariableBlock_block_desc_0 *desc, int *_number2, int _number3, NSMutableArray *_array, __Block_byref_number4_2 *_number4, int flags=0) : number2(_number2), number3(_number3), array(_array), number4(_number4->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
查看__ViewController__captureVariableBlock_block_impl_0
结构体中,int *_number2, int _number3, NSMutableArray *_array, __Block_byref_number4_2 *_number4,
,
注意:impl.isa = &_NSConcreteStackBlock
说明该block
是栈block
可见,对于
-
oc对象
NSMutableArray *_array
,捕获指针 -
局部静态变量
int *_number2
捕获指针 -
局部变量
int _number3
捕获值 - 全局变量并未捕获
- __block修饰的变量也是以指针形式捕获的,并且生成了一个新的结构体
struct __Block_byref_number4_2 {
void *__isa;
__Block_byref_number4_2 *__forwarding;
int __flags;
int __size;
int number4;
};
该结构体有个熟悉int number4
,即我们用__block修饰的变量
这里__forwarding是指向自身的(栈Block)
一般情况下,如果我们要对block捕获的局部变量进行赋值操作需要添加__block修饰符,而对全局变量,静态变量是不需要添加__block修饰符的(全局变量不需捕获,静态变量会捕获指针)
另外,block里访问self或self成员变量都会去截获self
可以看出,局部变量的值是block定义时的值,而不是block执行时的值
block在实现时就会对它引用到的它所在方法中定义的栈变量进行一次只读拷贝,然后在block内部使用该只读拷贝。换句话说,block截获自动变量的初始值,或者block捕获的是自动变量的副本
由于blick捕获了自动变量的瞬时值,所以在执行block语法后,即使改写block使用的自动变量的值也不会影响block执行时自动变量的值,所以上面局部变量的值打印是10
捕获特性
全局变量、静态全局变量不捕获,直接取值
局部变量是
基本数据类型
时,捕获值
局部变量是oc对象
时,连同所有权修饰符一起捕获
局部静态变量,捕获其指针(上述numer2打印为10)
网友评论