iOS小記--block的循環(huán)引用問題以及block何時需要/不需要使用[weak self]

其實這個問題不需要說太多了,只需要抓住會造成循環(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.bottomViewbottomView.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)的處理方式.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容