1.Block的分类
常见的三种block
- 1.全局block : NSGlobalBlock
- (void)viewDidLoad {
[super viewDidLoad];
void (^block)(void) = ^{
NSLog(@"我是全局block");
};
block();
NSLog(@"%@",block);
}
//打印结果
//Block探析[2300:140310] 我是全局block
//Block探析[2300:140310] <__NSGlobalBlock__: 0x10d14e038>
- 2.堆区block : NSMallocBlock
- (void)viewDidLoad {
[super viewDidLoad];
int a = 10;
void (^block)(void) = ^{
NSLog(@"我是堆区block---%d",a);
};
block();
NSLog(@"%@",block);
}
//打印结果:
//Block探析[2416:146513] 我是全局block---10
//Block探析[2416:146513] <__NSMallocBlock__: 0x600003db08d0>
- 3.栈区block : NSStackBlock (block在被copy之前就是栈区block)
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"%@",^{
NSLog(@"我是栈区block");
});
}
//打印结果:
//Block探析[2460:149166] <__NSGlobalBlock__: 0x109b7b038>
2.Block捕获外部变量
1.无__block修饰的局部变量
#include <stdio.h>
int main() {
int a = 10;
void (^block)(void) = ^{
printf("我是block---%d",a);
};
block();
return 0;
}
//clang生成.cpp文件
//下面是cpp文件中main函数的实现代码
int main() {
int a = 10;
/**
//我们上下代码对比一下
//下面这句代码是block的声明
//传入了三个参数
//第一个__main_block_func_0 block的代码块
//第二个__main_block_desc_0_DATA
//第三个为a的值
//void (*block)(void) = __main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a);
//这里面传入的a为外部变量的值
//(void *)__main_block_func_0:就是下面的结构
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
//这里把block声明时内部保存的a值给了一个临时变量a
//这个a和我们block访问的外部变量不是同一个值
//这里做的只是浅拷贝,拷贝了a的值
//所以我们在内部修改a的值时,外部的a是不会改变的
int a = __cself->a; // bound by copy
printf("我是全局block---%d",a);
}
//这里将我们block代码块里面的代码给了函数__main_block_func_0
//__main_block_impl_0:构造函数,返回值为一个block!
//block的本质:结构体
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
//声明的block内部会生成一个 int a 变量用来保存外部捕获的变量值!
int a;
//这里fp传入的值就是__main_block_func_0
//desc是__main_block_desc_0_DATA
//int _a 就是 a
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
//此时这里还是栈区block,后期会研究一下在什么时候变成了堆区block
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
//将代码块赋值给了impl的FuncPtr
impl.FuncPtr = fp;//函数式保存
Desc = desc;
}
};
*/
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
/**
//block结构体中的impl结构
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;//代码块的函数式保存
};
//去掉强转的代码,这里不就是block的调用:
block->FuncPtr(block);
*/
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
2.__block修饰的局部变量
#include <stdio.h>
int main() {
__block int a = 10;
void (^block)(void) = ^{
a++;
printf("我是全局block---%d",a);
};
block();
return 0;
}
//clang生成.cpp文件
//下面是cpp文件中main函数的实现代码
int main() {
//__block int a = 10;的代码处理
//__Block_byref_a_0类型的a
/**
这个与下面的__Block_byref_a_0 a 初始化对比
struct __Block_byref_a_0 {
void *__isa;//0
__Block_byref_a_0 *__forwarding;//a的地址指针
int __flags;//0
int __size;//空间大小
int a;a的值
};
*/
__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {
(void*)0,
(__Block_byref_a_0 *)&a,
0,
sizeof(__Block_byref_a_0),
10};
//
/**
这里block初始化的时候,传进来的是__Block_byref_a_0结构体类型的指针a;
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_a_0 *a; // by ref
//初始化的时候,传入的就是保存了a的地址指针及值的一个结构体的指针__Block_byref_a_0 *_a
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
*/
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
//调用
/**
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
//深拷贝,地址
//__Block_byref_a_0类型的a中__forwarding指针指向的a值
//a->__forwarding->a的值++
(a->__forwarding->a)++
__Block_byref_a_0 *a = __cself->a;
printf("我是全局block---%d",(a->__forwarding->a));
}
*/
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
3.Block循环引用
- (void)viewDidLoad {
[super viewDidLoad];
self.name = @"iOS";
self.block = ^{
NSLog(@"我是---%@",self.name);
};
self.block();
}
//这样一段代码,不用说,都知道会发生循环应用
//这是因为self->block->self形成了一个闭环,并且都是强持有!
//所以这里vc和block都无法释放
//也就是说,只要打破这个循环,不会循环应用了!
//我们平时最多的使用是
//定义一个弱引用weakSelf,指针指向self,因为是weak修饰,所以不对引用计数做任何操作
//__weak typeof(self) weakSelf = self;只需将self.name改为weakSelf.name就可打破循环
//这里使用了中介者设计模式
//weakSelf(弱引用表)->self->block->weakSelf
//看需求及逻辑需要,只有这一句代码有可能会有问题,
//比如把上面代码改为:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.name = @"iOS";
__weak typeof(self) weakSelf = self;
self.block = ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"我是---%@",weakSelf.name);
});
};
self.block();
}
- (void)dealloc {
NSLog(@"释放了");
}
//打印结果就是:
//Block探析[3895:288440] 释放了
//Block探析[3895:288440] 我是---(null)
//打印的时候,vc已经释放,属性name肯定也被释放了
//如果遇到这种情况,我们还需要做一步操作:
//__strong typeof(weakSelf) strongSelf = weakSelf;
//weakSelf.name改为strongSelf.name
//strongSelf临时变量出了作用域会被释放
//strongSelf->weakSelf(弱引用表)->self->block->weakSelf->strongSelf
//延迟释放vc
//Block探析[3922:291133] 我是---iOS
//Block探析[3922:291133] 释放了
这里简单的介绍一下block,下一篇会从底层出发,揭开block的神秘面纱…