Block
本质
本质是一个OC对象,内部有一个isa指针
封装了函数调用以及函数调用环境的OC对象
block底层结构如下图所示
底层结构
变量捕获
为了保证block内部能够正常访问外部变量,block有个变量捕获机制
变量类型 | 捕获到block内部 | 访问方式 | |
局部变量 | auto | ✅ | 值传递 |
static | ✅ | 指针传递 | |
全局变量 | ❌ | 直接访问 |
block的类型
block有3中类型,可以通过调用class的方法或者isa指针查看具体类型,最终都是继承自NSBlock
__NSGlobalBlock__
(_NSConcreateGlobalBlock):没有访问auto变量__NSStackBlock__
(_NSConcreateStackBlock):访问auto变量,存放于栈,随时可能被销毁__NSMallocBlock__
(_NSConcreateMallocBlock):__NSStackBlock__
调用了copy
内存分配
- .text区:代码段
- .data区:数据段,一般存放全局变量
- 堆:动态分配内存,需要程序员申请内存,也需要自己管理内存
- 栈:系统自动分配内存
block的copy
MRC
每一种类型的block调用copy后的结构如下
Block的类 | 副本源的配置存储域 | 复制效果 |
---|---|---|
_NSConcreateGlobalBlock | 程序的数据区域 | 什么也不做 |
_NSConcreateStackBlock | 栈 | 从栈复制到堆 |
_NSConcreateMallocBlock | 堆 | 引用计数增加 |
建议写法
@property (nonatomic, copy) void (^block)(void);
ARC
- 在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如
- block作为函数返回值时
- 将block赋值给
__strong
指针时 - block作为Cocoa API中方法名含有usingBlock的方法参数时
- block作为GCD API方法参数时
- 建议写法
@property (strong, nonatomic) void (^block)(void);
@propertu (copy, nonatomic) void (^block)(void);
对象类型的auto
当block内部访问了对象类型的auto变量时
- 如果block在栈上, 将不会对auto变量产生强引用
如果block被拷贝到堆上
- 堆调用block内部的copy函数
- copy函数内部会调用_Block_object_assign函数
- _Block_object_assign函数会根据auto变量的修饰符(
__strong
,__weak__
,__unsafe_unretained
)做出相应的操作,类似于retain(形成强引用、弱引用)
如果block从堆上移除
- 会调用gblock内部的dispose函数
- dispose函数内部会调用_Block_object_dispose函数
- _Block_object_dispose函数会自动书房引用的auto变量,类似于release
函数 | 调用时机 |
---|---|
copy函数 | 栈上Block复制到堆上时 |
dispose函数 | 堆上的Block被废弃时 |
__block
本质
- __block用于解决block内部无法修改auto变量值的问题
- __block不能修饰全局变量、静态变量(static)
- 编译器会将__block变量包装成一个对象
内存管理
- 当block在栈上时,并不会对__block变量产生强引用
- 当block呗copy到堆时
- 会调用block内部的copy函数
- copy函数会调用_Block_object_assign函数
- _Block_object_assign函数会对__block变量强引用
循环引用
ARC
通过
__weak
解决,推荐- 不会产生强引用,指向的对象销毁时会自动置为nill
1
2
3
4__weak typeof(self) weakSelf = self;
self.bolck = ^{
NSLog(@"%@", weakSelf);
};通过
__unsafe_unretaine
解决- 不会产生强引用,不安全(野指针)。指向的对象销毁时,指针指向的地址值不变
1
2
3
4__unsafe_unretaine typeof(self) weakSelf = self;
self.bolck = ^{
NSLog(@"%@", weakSelf);
};通过
__block
解决- 必须要调用block
1
2
3
4
5
6__block typeof(self) blockSelf = self;
self.block = ^{
NSLog(@"%@", blockSelf);
blockSelf = nil;
};
self.block();
MRC
通过
__unsafe_unretaine
解决,同ARC1
2
3
4__unsafe_unretaine typeof(self) weakSelf = self;
self.bolck = ^{
NSLog(@"%@", weakSelf);
};通过
__block
解决,同ARC1
2
3
4
5
6__block typeof(self) blockSelf = self;
self.block = ^{
NSLog(@"%@", blockSelf);
blockSelf = nil;
};
self.block();
相关问题
block底层原理?
封装了函数调用及其调用环境的oc对象
__block的作用,使用注意点?
用于解决block内部无法修改auto变量值的问题
__block修饰为什么是copy,使用注意点?
block一旦没有进行copy操作,就不会在堆上。
需要注意循环引用