block循環引用的三種情況和處理辦法


剛入職在看已經上線的項目,其中用到block進行快捷回調的做法很常用,但是Xcode都給給以了如下【循環引用】的警告(如下)的情況,結合網絡上的查找和自己的理解,進行總結如下。

//

Capturing 'self' strongly in this block is likely to lead to a retain cycle

 

出現這種情況的原因主要是:因為block的特性,聲明block的對象都會以copy的形式持有block,而block對於它內部的對象也會進行強引用,從而導致了循環引用。下面舉具體的例子來說明。

1、self的block屬性內部引用self,互相引用;

self.myTitle = self.title;

    self.backViewController = ^{
        [self backProViewController];//【【【循環引用】】】
};

2、該例子中showImage為控制器方法內的本地(局部)變量,showImage強持有block,block再強持有showImage,導致循環引用

 1 -(void)imageTapCilck
 2 {
 3     NSString* imageUrl = [dataSoure objectForKey:@"chargeStandard"];
 4     if (imageUrl) {
 5         FLYShowImageViewController* showImage = [[FLYShowImageViewController alloc]initWithNibName:@"FLYShowImageViewController" bundle:nil];
 6         showImage.imageUrl = imageUrl;
 7         showImage.cancel = ^{
 8             [showImage dismissViewControllerAnimated:NO completion:^{//【【【循環引用】】】
 9 10  }]; 11  }; 12 [self presentViewController:showImage animated:NO completion:^{ 13 14  }]; 15  } 16 17 }

3、下面的例子中有2個循環引用警告,例中topView、_scrollView均為實例(成員)變量;

第一個:實例變量topView強持有block,block強持有實例變量_scrollView,而控制器強持有兩個實例變量topView和_scrollView,形成循環引用;

第二個self強持有block,block強持有實例變量_scrollView,self強持有實例變量_scrollView,形成循環引用

 1 -(void)initTopView
 2 {
 3     topView = [[FLYConferenceCallSegmentView alloc]initWithFrame:CGRectMake(0, 0, self.frame.size.width, 70)];
 4     NSDictionary* dic1 = @{@"title":@"集團通訊錄",@"url":@"group_contact_normal",@"selected_url":@"group_contact_pressed"};
 5     NSDictionary* dic2 = @{@"title":@"手機通訊錄",@"url":@"local_contact_normal",@"selected_url":@"local_contact_pressed"};
 6     NSDictionary* dic3 = @{@"title":@"手工輸入",@"url":@"manual_normal",@"selected_url":@"manual_pressed"};
 7     NSArray* array = @[dic1,dic2,dic3];
 8     topView.selectForIndex = ^(NSInteger index){//【【【循環引用1】】】
 9         _scrollView.contentOffset = CGPointMake(index*_scrollView.frame.size.width, 0);
10     };
11     topView.array = array;
12     [topView reloadView];
13     
14     self.selectTest = ^(NSInteger index){
15         _scrollView.contentOffset = CGPointMake(index*_scrollView.frame.size.width, 0);//【【【循環引用2】】】
16  }; 17 18  [self addSubview:topView]; 19 20 }

在使用block進行回調時,出現這樣的情況是難免的,有時候可能還會在block內部強引用多個實例變量和self,解決的辦法是一樣的,即在block的內部使用弱引用修飾符__weak,針對上面的例子,舉例如下:

如果block持有self,使用下面語句獲取self的弱引用weakSelf替換block中的self即可;

__weak typeof(self) weakSelf = self;

同理,其他被block強引用的對象都需要獲取弱引用后代入block。

__weak typeof(UIScrollView *) weak_scrollView = _scrollView;

 

一個疑問:有另外一個做法是:在獲取對象的弱引用后,在block的內部再轉成強引用在block中使用,這樣的最終結果會怎么樣,不得而知。

    __weak typeof(self) weakSelf = self;

    topView.selectForIndex = ^(NSInteger index){
        __strong typeof(self) strongSelf = weakSelf;
        [strongSelf initScrollView];
    };

 

 

一個關於block的詳細解讀請參考:Block的引用循環問題 (ARC & non-ARC)【wildfireli】 。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM