Block 深入讲解,变量截获以及__Block 修饰符原理
block:block 是将函数及其上下文封装起来的对象
测试代码
- (void)testBlock{
int i = 2;
int (^block)(int) = ^ int (int num){
return num * i;
};
block(2);
}
然后通过系统命令将他转化为C++代码,clang -rewrite-objc block.m
,然后会生成一个 cpp 文件,打开cpp文件,找打如下格式的方法_I_block_testBlock
, -I
表示实例方法,后面是类名,后面是方法名
static void _I_block_testBlock(block * self, SEL _cmd) {
int i = 2;
int (*block)(int) = ((int (*)(int))&__block__testBlock_block_impl_0((void *)__block__testBlock_block_func_0, &__block__testBlock_block_desc_0_DATA, i));
((int (*)(__block_impl *, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 2);
}
__block__testBlock_block_func_0
是一个函数指针,第二个 __block__testBlock_block_desc_0_DATA
是方法描述,最后一个是我们的局部变量也传进来了 i
, block 定义的变量其实是个函数指针,
struct __block__testBlock_block_impl_0 {
struct __block_impl impl;
struct __block__testBlock_block_desc_0* Desc;
int i;
__block__testBlock_block_impl_0(void *fp, struct __block__testBlock_block_desc_0 *desc, int _i, int flags=0) : i(_i) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
__block_impl :
struct __block_impl {
void *isa;//isa 指针
int Flags;
int Reserved;
void *FuncPtr;// 函数指针
};
其中第一个为 isa 指针,证明block是对象的标志
总结 :
block 其实就是对象,封装了函数和上下稳定,block调用就是函数的调用
截获变量
int i = 2;
int (^block)(int) = ^ int (int num){
return num * i;
};
i = 10;
block(2);
输出值是多少?
局部变量类型,有基本数据类型和对象类型,静态局部变量,全局变量,静态全局变量
1.对于基本数据类型的局部变量截获其值
2.对于对象类型的局部变量连同所有权修饰符一起截获
3.以指针形式截获局部静态变量
4.不截获全局变量和静态全局变量
例子:
//
// block.m
// blogTest
//
// Created by 孙承秀 on 2019/12/15.
// Copyright © 2019 孙承秀. All rights reserved.
//
#import "block.h"
// 全局变量
int global_int = 5;
// 静态全局变量
static int static_global_int = 6;
@implementation block
- (void)testBlock{
// 基本数据类型
int i = 2;
// 对象类型的局部变量
__unsafe_unretained id unsafe_obj = nil;
__strong id strong_obj = nil;
// 局部静态变量
static int static_int = 3;
void (^block)(void) = ^ {
NSLog(@"局部变量:%d",i);
NSLog(@"局部变量(unsafe_unretained):%@",unsafe_obj);
NSLog(@"局部变量(strong):%@",strong_obj);
NSLog(@"局部静态变量:%d",static_int);
NSLog(@"全局变量:%d",global_int);
NSLog(@"全局静态变量:%d",static_global_int);
};
block();
}
@end
使用命令将其转化为c++代码 clang -rewrite-objc -fobjc-arc block.m
struct __block__testBlock_block_impl_0 {
struct __block_impl impl;
struct __block__testBlock_block_desc_0* Desc;
// 截获局部变量的值
int i;
//连同所有权修饰符一起截获
__unsafe_unretained id unsafe_obj;
__strong id strong_obj;
// 以指针形式截获局部变量
int *static_int;
// 没有全局变量和静态全局变量,说明不截获
__block__testBlock_block_impl_0(void *fp, struct __block__testBlock_block_desc_0 *desc, int _i, __unsafe_unretained id _unsafe_obj, __strong id _strong_obj, int *_static_int, int flags=0) : i(_i), unsafe_obj(_unsafe_obj), strong_obj(_strong_obj), static_int(_static_int) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
通过查看源码,可以看到 block 的结构体里面,i 被直接截获值,我们定义的连个对象,连同修饰符都被截获进去,而局部静态变量是以指针的形式截获.
练习题
int i = 2;
int (^block)(int) = ^ int (int num){
return num * i;
};
i = 10;
block(2);
static int i = 2;
int (^block)(int) = ^ int (int num){
return num * i;
};
i = 10;
block(2);
所以这两个题的返回结果就不用我说了吧,没有加 static 就是普通的局部变量,block 内部只截获他的值,之后修改就不在改变,而加了static修饰符之后,是以指针的形式截获,那么之后修改值也会生效。
__Block 修饰符
一般情况下,对被截获的变量进行赋值操作需要添加 __Block 修饰符
NSMutableArray *arr = [NSMutableArray array];
void (^block)(void)=^{
[arr addObject:@"123"];
};
block();
NSMutableArray *arr1 = nil;
void (^block1)(void)=^{
arr1 = [NSMutableArray array];
[arr1 addObject:@"456"];
};
block1();
上面两个题打印结果又是如何呢?答案是第二个,需要加上 __Block , 因为赋值需要加上,__Block
,
那什么时候不需要加上 __Block 修饰符呢?读了前面就一定知道了
1.静态局部变量
2.全局变量
3.静态全局变量
下面的例子的值:
__block int blokck_value = 2;
int (^block)(int) = ^ int (int num){
return num * blokck_value;
};
blokck_value = 10;
block(2);
答案是20,被__Block修饰的变量最后变成了对象, blokck_value = 10,其实最后就变成了blokck_value ._forwarding->multiplier = 10
,通过forwarding指针改变他的值
网友评论