其实这个问题不需要说太多了,只需要抓住会造成循环引用的本质原因就可以了.
如果block没有直接或者间接被self存储,就不会产生循环引用。就不需要用weak self####
引发循环引用,是因为当前self在强引用着block,而block又引用着self,这样就造成了循环引用。而需不需要使用[weak self]就是由循环引用来决定,如果造成了循环引用,就必须使用[weak self]来打破循环.
1.直接强引用####
来分析一个自己设计的block模块:
// 这种情况不必要弱引用 [self oneBlockSucess:^{ [self doSomething]; }]; //这种情况就有必需用weakself来打破引用环 self.secondBlock = ^{ [self doSomething]; };
self.secondBlock
说明当前self持有了secondBlock这个block属性,block属性是由copy来声明的,@property(nonatomic, copy)BtnSecondBlockBlock secondBlock;
属于对象self的强引用属性.
所以如果在block回调内想拿到self去做一些业务处理时,如果直接使用self,就会造成block持有了self,两者互相持有,造成循环引用.打破这个环,就要使用如typeof(self) __weak weakSelf = self;
的weakSelf方式;
2.间接强引用####
再来分析一个自己设计的block模块:
这是一个持有block的view: XXSubmitBottomView
typedef void(^BtnPressedBlock)(UIButton *btn);
@interface XXSubmitBottomView : UIView
@property(strong,nonatomic)UILabel *allPriceLab;
@property(strong,nonatomic)UIButton *submittBtn;
@property(nonatomic, weak)XXConfirmOrderController *currentVc;
@property(nonatomic, weak)XXConfimOrderModel *model;
@property(nonatomic, copy)BtnPressedBlock block;
-(void)submittBtnPressed:(BtnPressedBlock)block;
@end
这是一个持有bottomView属性的控制器: XXConfirmOrderController
@interface XXConfirmOrderController ()
@property(nonatomic, strong) XXConfimOrderTableView *tableView;
@property(nonatomic, strong) XXSubmitBottomView *bottomView;
@property(nonatomic, strong) XXConfimOrderModel *confimModel;
@end
@implementation XXConfirmOrderController
-(void)viewDidLoad{
[super viewDidLoad];
self.title = @"确认下单";
self.view.backgroundColor = DDCJ_Gray_Color;
//UI
[self.view addSubview:self.tableView];
[self.view addSubview:self.bottomView];
//Data
[self loadData];
}
下面是self.bottomView的懒加载以及block的回调处理
-(XXSubmitBottomView *)bottomView{
if (!_bottomView) {
_bottomView = [[XXSubmitBottomView alloc] initWithFrame:CGRectMake(0, self.view.height - 50, Width, 50)];
_bottomView.currentVc = self;
#warning self.bottomView.block self间接持有了BtnPressedBlock 必须使用weak!
WEAKSELF //ps: weakSelf的宏定义#define WEAKSELF typeof(self) __weak weakSelf = self;
[_bottomView submittBtnPressed:^(UIButton *btn) {
NSLog(@"do提交订单");
MBProgressHUD *hud = [MBProgressHUD showMessage:@"加载中..." toView:weakSelf.view];
NSMutableDictionary *dynamic = [NSMutableDictionary dictionary];
[dynamic setValue:weakSelf.confimModel.orderRemark forKey:@"orderRemark"];
if (weakSelf.agreementId) {
[dynamic setValue:weakSelf.agreementId forKey:@"agreementId"];
}
if (weakSelf.isShoppingCartEnter) {
[dynamic setValue:@"0" forKey:@"orderOrigin"];
}else{
[dynamic setValue:@"1" forKey:@"orderOrigin"];
}
[[APIClientFactory sharedManager] requestConfimOrderWithDynamicParams:dynamic success:^(NSMutableArray *dataArray) {
[hud hideAnimated:YES];
[weakSelf handlePushControllerWithModelList:dataArray];
} failure:^(NSError *error) {
[hud hideAnimated:YES];
[MBProgressHUD showError:error.userInfo[@"message"]];
}];
}];
}
return _bottomView;
}
这里的warning信息其实已经写的很清楚了,#warning self.bottomView.block self间接持有了BtnPressedBlock 必须使用weak!
此处的控制器self并没有直接持有block属性,但是却强引用了bottomView,bottomView强引用了block属性,这就造成了间接循环引用. block回调内必须使用[weak self]来打破这个循环,否则就会导致这个控制器self永远都不会被释放掉产生常驻内存。如果self.bottomView
与bottomView.block
有一环不是强引用,就不会产生循环引用问题,因为不会形成一个引用环. 如果一个应用程序里面你有很多循环引用,那么内存占用就会比较大,并且由于一些特殊操作,会产生一个控制器的N个对象实例常驻内存无法释放,造成大量的系统内存泄漏,这当然是谁都不想看到的结果.#####
打破这个环,就要使用如typeof(self) __weak weakSelf = self;
的weakSelf方式;
自己设计的block模块都可以在合适时机进行打断。打断的方式就是使用self的弱引用即可.
如果是对系统类加扩展方法导致的循环引用,那么需要找得到合适的时机打断,也是没问题的。
另外有个简单的方法可以绕过这个问题,如果self引用了一个block,block又需要调用self,可以
把self通过参数回传给block,这样就不会产生循环引用了。
block回传的self可以声明成id类型,这样使用的时候可以在入参声明具体self类型,避免显式类型转换,方便开发。
typedef void (^Block) (id selfRef);
Block block = ^(XXX *selfRef){
};
这是一种比较巧妙一点的处理方式.