block代碼塊在開發中經常使用於異步開發,例如GCD就是提供block的異步塊,同時在使用block的時候每每須要注意避免循環引用,而消除block循環引用就是靠__weak來實現,好比:git
__weak typeof(self) _self = self;
而後在block塊中使用__weak的_self這樣就避免了循環引用的問題。
同時爲了保證在block塊中self不被釋放掉,每每在block塊中添加__strong修飾的引用github
__strong typeof(_self) self = _self;
這樣既避免了循環引用,同時也保障了block塊中使用self的時候不被意外釋放掉。app
__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加載後去下載一張大圖片,而後動態的添加這個圖片到視圖,圖片定位使用約束定位,簡略的場景以下動圖:ide
潛在崩潰:在大圖尚未下載完以前就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:ui
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了致使崩潰。spa
在使用block解決循環引用別忘記判斷self是否爲空。code
__weak typeof(self) _self = self; [self xxxfunciotnCompletion:^(void){ __strong typeof(_self) self = _self; if(!self) return; }];
測試demo在這裏Github圖片