美文网首页iOS高级技术首页投稿(暂停使用,暂停投稿)iOS Developer
iOS小记--block的循环引用问题以及block何时需要/不

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

作者: inxx | 来源:发表于2017-01-10 17:38 被阅读1490次

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

    如果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:@万里长空 嗯那就好,我只是猜测,引用应该确实产生了,但是手动置空之后相互引用的一端销毁了,其实最终的目的就是要在ARC 环境下退出的时候自动释放内存,只要目的达到了,方式可以有很多
      • 万里长空:如果block为成员变量,在block中也使用了self,并且self没有做__weak处理,执行完将这个成员变量置空(NULL)能不能打破这个循环引用呢。
        万里长空:@inxx self.block = nil 会解除self对block的持有,没问题,会执行dealloc这个方法。
        inxx:@万里长空 我认为引用已经产生了,你可以用退出控制器的方式验证一下,调用-(Void)dealloc{}方法打印点东西,看看退出控制器的时候有没有执行到这个方法,就知道这种方式可不可以了。

      本文标题:iOS小记--block的循环引用问题以及block何时需要/不

      本文链接:https://www.haomeiwen.com/subject/xookbttx.html