一、循环引用
原因是使用MRC管理内存时,Block的内存管理需要区分是Global(全局)、Stack(栈)还是Heap(堆),而在使用了ARC之后,苹果自动会将所有原本应该放在栈中的Block全部放到堆中。全局的Block比较简单,凡是没有引用到Block作用域外面的参数的Block都会放到全局内存块中,在全局内存块的Block不用考虑内存管理问题。(放在全局内存块是为了在之后再次调用该Block时能快速反应,当然没有调用外部参数的Block根本不会出现内存管理问题)。
所以Block的内存管理出现问题的,绝大部分都是在堆内存中的Block出现了问题。默认情况下,Block初始化都是在栈上的,但可能随时被收回,通过将Block类型声明为copy类型,这样对Block赋值的时候,会进行copy操作,copy到堆上,如果里面有对self的引用,则会有一个强引用的指针指向self,就会发生循环引用,如果采用weakSelf,内部不会有强类型的指针,所以可以解决循环引用问题。
那是不是所有的block都会发生循环引用呢?其实不然,比如UIView的类方法Block动画,NSArray等的类的遍历方法,也都不会发生循环引用,因为当前控制器一般不会强引用一个类。
二、解决方案
参照libextobjc中的weakify和strongify。
@weakify(self)
[self.LoadView setRetryBlock:^{
@strongify(self)
[self.foo load:self.data];
}];
三、经典案例
代码片段ModelOne.h
#import <Foundation/Foundation.h>
typedef void (^ModelOneBlock) (void);
@interface ModelOne : NSObject
- (void)cycle:(ModelOneBlock)block;
- (void)compare:(ModelOneBlock)block;
@end
代码片段ModelOne.m
#import "ModelOne.h"
@interface ModelOne ()
@property (nonatomic, copy) ModelOneBlock block;
@end
@implementation ModelOne
- (void)cycle:(ModelOneBlock)block{
_block = block;
if (_block){
_block();
}
}
- (void)compare:(ModelOneBlock)block{
if (block){
block();
}
}
@end
循环引用CycleOneViewController
#import "CycleOneViewController.h"
#import "ModelOne.h"
@interface CycleOneViewController ()
@property (nonatomic, strong)ModelOne *model;
@end
@implementation CycleOneViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.model cycle:^{
self.view.backgroundColor = [UIColor yellowColor];
}];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)dealloc{
NSLog(@"dealloc");
}
- (ModelOne *)model{
if(!_model){
_model = [ModelOne new];
}
return _model;
}
对比CompareOneViewController
#import "CompareOneViewController.h"
#import "ModelOne.h"
#import "BlockConstants.h"
@interface CompareOneViewController ()
@property (nonatomic, strong)ModelOne *model;
@end
@implementation CompareOneViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.model compare:^{
self.view.backgroundColor = [UIColor yellowColor];
}];
@weakify(self);
[self.model cycle:^{
@strongify(self);
self.view.backgroundColor = [UIColor yellowColor];
}];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)dealloc{
NSLog(@"dealloc");
}
- (ModelOne *)model{
if(!_model){
_model = [ModelOne new];
}
return _model;
}
CycleOneViewController是一个比较典型的循环引用。CompareOneViewController是没有循环引用的。Demo地址
四、解析
1、首先观察一下类ModelOne的两个方法。
- (void)cycle:(ModelOneBlock)block
会持有block,给成环提供了必要条件。
- (void)compare:(ModelOneBlock)block
不会持有block,该方法没有循环引用的风险。
2、外部斩断环
类CompareOneViewController在调用cycle方法时,用weakself替换了self,将强引用strong变成了弱引用weak。从外部斩断了成环的可能性。
@weakify(self);
[self.model cycle:^{
@strongify(self);
self.view.backgroundColor = [UIColor yellowColor];
}];
Tips:使用weakify和strongify时注意,block内部不能使用下划线方式,会产生循环引用。
@weakify(self);
[self.model cycle:^{
@strongify(self);
_dataView.backgroundColor = [UIColor yellowColor];
//self.dataView.backgroundColor = [UIColor yellowColor];
}];
五、检测
Xcode->build setting->implicit retain of ‘self’ within blocks
摘要
https://juejin.im/post/58ca0832a22b9d006418fe43
https://github.com/jspahrsummers/libextobjc