手记

iOS小记--block的循环引用问题以及block何时需要/不需要使用[weak self]

其实这个问题不需要说太多了,只需要抓住会造成循环引用的本质原因就可以了.

如果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.bottomViewbottomView.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){
};

这是一种比较巧妙一点的处理方式.

              


作者:inxx
链接:https://www.jianshu.com/p/fd9c67db2e9f


0人推荐
随时随地看视频
慕课网APP