本文将介绍block的类型,循环引用的解决方法以及block底层分析
Block简介
Block定义:带有自动变量的匿名函数,它是C语言的拓展功能,之所以是扩展,是因为C语言不允许存在这样的匿名函数
- 匿名函数
- 匿名函数式指不带函数名称的函数
- 带有自定变量
- Block拥有捕获外部变量的功能,在Block中访问一个外部的局部变量,Block会持有它的临时状态,自动捕获变量值,外部局部变量的变化不会影响它的状态(这个下面会讲到)。
Block类型
作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发交流群:130 595 548,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)
block主要有三种类型
- 1.
__NSGlobalBlock__
:全局block,存储在全局区
此时block无参也无返回值,属于全局block
- 2.
__NSMallocBlock__
:堆区block,因为block既是函数,也是对象
此时block会访问外界变量,即底层拷贝a,所以是堆区block
- 3.
__NSStackBlock__
:栈区block
其中局部变量a在没有处理之前(即没有拷贝之前)是 栈区block, 处理后(即拷贝之后)是堆区block ,所以栈区block越来越少了
这个情况下,可以通过__weak不进行强持有,block就还是栈区block
总结
- 1.block是直接存储在
全局区
- 2.block如果
访问外界变量
,并进行block相应copy
- 如果此时的
block是强引用
,则block存储在堆区
,即堆区block - 如果此时的
block通过——weak变成了弱引用
,则block存储在栈区
,即栈区block
- 如果此时的
Block循环引用
【正常释放】:当A持有B
,当A调用dealloc方法
,给B发送release
信号,B收到release信号
,如果此时B的引用计数为0
时,则会调用B的dealloc方法
,此时A,B都能正常释放
【循环引用】:当A持有B
,B同时也持有A时
,此时A销毁需要B先销毁
,而B销毁同样需要A先销毁
,就导致相互等待销毁
,此时A,B的引用计数都不为0
,所以A,B此时都无法释放
。
解决循环引用
举个循环引用的例子:如下图
上面代码发生了
循环引用
,因为在block内部使用了self的name变量
,导致block持有self
,而self本来就持有block
,就导致了self和block相互持有
。
下面来解决循环引用
- 1.
weak-strong-dance
(最常用的方法) - 2.
__block修饰对象,同时置nil
- 3.
传递对象self作为block的参数,提供给block内部使用
- 4.
使用NSProxy
weak-strong-dance(弱强共舞)
- 1.
如果block内部并未嵌套block,直接使用__weak修饰self即可
@interface ViewController ()
@property (nonatomic, copy) void (^block)(void);
@property (nonatomic, copy) NSString *name;
@end
- (void)viewDidLoad {
[super viewDidLoad];
self.name = @"man";
__weak typeof(self) weakSelf = self;
self.block = ^(void){
NSLog(@"%@",weakSelf.name);
};
self.block();
}
由于此时的weakSelf和self指向同一片内存空间
,而且使用__weak不会导致self的引用计数发生变化
,可以通过打印weakSelf和self的指针地址
,以及self的引用计数来验证 [图片上传失败…(image-74b93d-1619163053376)]
- 2.如果
block内部嵌套block
,则需要同时使用__weak和__strong
如果只用weak修饰
,则可能出现block内部持有的对象被提前释放
,为了防止block内部变量被提前释放
,使用strong对引用计数+1
,防止提前释放
。
其中strongSelf是一个临时变量
,在block的作用域内
,当block执行完就会释放strongSelf
,这种方式属于打破self对block的强引用
,依赖于中间者模式
,属于自动置为nil
,也就是自动释放
。
__block修饰变量
这种方式同样依赖于中介者模式
,属于手动释放
,是通过__block修饰对象
,主要是因为__block修饰的对象是可以改变的
。
这里的
block必须调用
,如果不调用block
,vc就不会置空
,那么依旧是循环引用
,self和block都不会释放<