其實這個問題不需要說太多了,只需要抓住會造成循環(huán)引用的本質(zhì)原因就可以了.
如果block沒有直接或者間接被self存儲,就不會產(chǎn)生循環(huán)引用。就不需要用weak self####
引發(fā)循環(huán)引用,是因為當(dāng)前self在強(qiáng)引用著block,而block又引用著self,這樣就造成了循環(huán)引用。而需不需要使用[weak self]就是由循環(huán)引用來決定,如果造成了循環(huán)引用,就必須使用[weak self]來打破循環(huán).
1.直接強(qiáng)引用####
來分析一個自己設(shè)計的block模塊:
// 這種情況不必要弱引用 [self oneBlockSucess:^{ [self doSomething]; }]; //這種情況就有必需用weakself來打破引用環(huán) self.secondBlock = ^{ [self doSomething]; };
self.secondBlock說明當(dāng)前self持有了secondBlock這個block屬性,block屬性是由copy來聲明的,@property(nonatomic, copy)BtnSecondBlockBlock secondBlock;屬于對象self的強(qiáng)引用屬性.
所以如果在block回調(diào)內(nèi)想拿到self去做一些業(yè)務(wù)處理時,如果直接使用self,就會造成block持有了self,兩者互相持有,造成循環(huán)引用.打破這個環(huán),就要使用如typeof(self) __weak weakSelf = self;的weakSelf方式;
2.間接強(qiáng)引用####
再來分析一個自己設(shè)計的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 = @"確認(rèn)下單";
self.view.backgroundColor = DDCJ_Gray_Color;
//UI
[self.view addSubview:self.tableView];
[self.view addSubview:self.bottomView];
//Data
[self loadData];
}
下面是self.bottomView的懶加載以及block的回調(diào)處理
-(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信息其實已經(jīng)寫的很清楚了,#warning self.bottomView.block self間接持有了BtnPressedBlock 必須使用weak!
此處的控制器self并沒有直接持有block屬性,但是卻強(qiáng)引用了bottomView,bottomView強(qiáng)引用了block屬性,這就造成了間接循環(huán)引用. block回調(diào)內(nèi)必須使用[weak self]來打破這個循環(huán),否則就會導(dǎo)致這個控制器self永遠(yuǎn)都不會被釋放掉產(chǎn)生常駐內(nèi)存。如果self.bottomView與bottomView.block有一環(huán)不是強(qiáng)引用,就不會產(chǎn)生循環(huán)引用問題,因為不會形成一個引用環(huán). 如果一個應(yīng)用程序里面你有很多循環(huán)引用,那么內(nèi)存占用就會比較大,并且由于一些特殊操作,會產(chǎn)生一個控制器的N個對象實例常駐內(nèi)存無法釋放,造成大量的系統(tǒng)內(nèi)存泄漏,這當(dāng)然是誰都不想看到的結(jié)果.#####
打破這個環(huán),就要使用如typeof(self) __weak weakSelf = self;的weakSelf方式;
自己設(shè)計的block模塊都可以在合適時機(jī)進(jìn)行打斷。打斷的方式就是使用self的弱引用即可.
如果是對系統(tǒng)類加擴(kuò)展方法導(dǎo)致的循環(huán)引用,那么需要找得到合適的時機(jī)打斷,也是沒問題的。
另外有個簡單的方法可以繞過這個問題,如果self引用了一個block,block又需要調(diào)用self,可以
把self通過參數(shù)回傳給block,這樣就不會產(chǎn)生循環(huán)引用了。
block回傳的self可以聲明成id類型,這樣使用的時候可以在入?yún)⒙暶骶唧wself類型,避免顯式類型轉(zhuǎn)換,方便開發(fā)。
typedef void (^Block) (id selfRef);
Block block = ^(XXX *selfRef){
};
這是一種比較巧妙一點(diǎn)的處理方式.