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加载后去下载一张大图片,然后动态的添加这个图片到视图,图片定位使用约束定位,简略的场景如下动图:
潜在崩溃:在大图还没有下载完之前就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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。