block解除了循环引用后还需要注意

block循环引用

block代码块在开发中常用于异步开发,例如GCD就是提供block的异步块,同时在使用block的时候往往需要注意避免循环引用,而消除block循环引用就是靠__weak来实现,比如:

__weak typeof(self) _self = self;

然后在block块中使用__weak的_self这样就避免了循环引用的问题。
同时为了保证在block块中self不被释放掉,往往在block块中添加__strong修饰的引用

__strong typeof(_self) self = _self;

这样既避免了循环引用,同时也保障了block块中使用self的时候不被意外释放掉。

__weak typeof(self) _self = self;
[self xxxfunciotnCompletion:^(void){
    __strong typeof(_self) self = _self;
    //使用self不会存在循环引用了
}];

同时还应该注意另外一个细节问题,如果在异步的Completion:块还没有执行前self就被释放的问题, 因为block块在执行前是并没有强引用self,此时self是可能被释放的;如果此时再使用self就应该注意nil可能引起的崩溃。
因为__strong typeof(_self) self = _self;只能保障如果此时self没有被释放那么在block块中使用self期间不会被释放, 这只是暂时性的强引用self, 随着block块的结束而释放。

崩溃示例

present一个控制器BViewController,BViewController加载后去下载一张大图片,然后动态的添加这个图片到视图,图片定位使用约束定位,简略的场景如下动图:

《block解除了循环引用后还需要注意》

潜在崩溃:在大图还没有下载完之前就dismiss控制器BViewController使其销毁,待图片下载完成回调执行添加约束时崩溃。

崩溃分析

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    __weak typeof(self) _self = self;
    [[BigImageDownloader shareDownloader] requestBigImageWithUrl:@"BigImg" completion:^(UIImage * _Nonnull image) {
        __strong typeof(_self) self = _self;
        
        //创建一个UIImageView显示, 并添加水平居中约束
        UIImageView *bigImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 80, 200, 200)];
        bigImageView.translatesAutoresizingMaskIntoConstraints = NO;
        [self.view addSubview:bigImageView];
        bigImageView.image = image;
        
        //创建约束
        [NSLayoutConstraint constraintWithItem:self.view
                                     attribute:NSLayoutAttributeCenterX
                                     relatedBy:NSLayoutRelationEqual
                                        toItem:bigImageView
                                     attribute:NSLayoutAttributeCenterX
                                    multiplier:1.0f
                                      constant:0.0f].active = YES;
        
        [NSLayoutConstraint constraintWithItem:self.topLayoutGuide
                                     attribute:NSLayoutAttributeTop
                                     relatedBy:NSLayoutRelationEqual
                                        toItem:bigImageView
                                     attribute:NSLayoutAttributeTop
                                    multiplier:1.0f
                                      constant:-80.0f].active = YES;
    }];
}

进入controller后就开始去下载一个图片,然后把图片用约束的方式定位显示到按钮的上方;
崩溃路径:进入控制器后,在图片还没下载完前就dismiss控制器,使其销毁,待图片下载完成后崩溃
崩溃log:

2018-11-03 22:13:11.879093+0800 strongSelf[43867:2859446] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'NSLayoutConstraint for (null): Constraint must contain a first layout item'

根据以上信息,崩溃原因为创建约束时的first layout item不能为null, 而在此例中的first layout item即为self.view,也就是说self.view为nil了,因为我们在图片下载完成前就dismiss了也就被销毁了,所以self.view自然为nil了导致崩溃。

解决

在使用block解决循环引用别忘记判断self是否为空。

__weak typeof(self) _self = self;
[self xxxfunciotnCompletion:^(void){
    __strong typeof(_self) self = _self;
    if(!self) return;
}];

测试demo在这里Github

    原文作者:DandJ
    原文地址: https://segmentfault.com/a/1190000016906371
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞