1. 程式人生 > >iOS開發——Block引起迴圈引用的解決方案

iOS開發——Block引起迴圈引用的解決方案

       記憶體問題始終是軟體開發中的頭等大事,iOS開發中也不例外,在面試中也是必問的問題。今天我們主要來講講Block中涉及的迴圈引用問題。當我們自己一開始寫程式碼的時候,可能會大量在block中使用self,但是當看到別人優秀的程式碼的時候,發現別人常常不是用self,而使用weakSelf. 為什麼呢?本文的示例程式碼上傳至 https://github.com/chenyufeng1991/Block_WeakSelf 。 

       首先我先來說說記憶體管理的原則:

1.預設使用strong,可選weak。strong下不管成員變數還是屬性,每次使用指標指向一個物件,就會自動呼叫retain,並對舊物件呼叫release,在需要釋放的時候設為nil。

2.避免迴圈引用,否則手動設定nil釋放。

3.建立block匿名函式之前一般需要對self進行weak化,否則造成迴圈引用無法釋放controller。

      首先Xcode為我們提供了良好的編譯環境,如果程式碼中有可能出現迴圈引用的地方,Xcode會給我們警告:“Capturing 'self' strongly in this block is likely to lead to a retain cycle”.如圖:


      block中的迴圈引用是這樣的:某個物件有一個copy或者strong成員變數或者屬性,這時block內部直接引用了成員變數或者self,這樣就產生了self持有block成員,block成員持有self,就會導致迴圈引用。因為self本身就是一個strong型別的變數。蘋果官方的建議是:傳進block之前,把self轉換成weak automatic的變數,這樣在block中就不會出現對self的強引用。如果在block執行完成之前,self被釋放,weakSelf也會置為nil。weak型別相對比較安全,因為可以在釋放後自動置為nil,不會引起野指標。那麼如何來宣告呢?

 1.

__weak typeof(self) weakSelf = self;

這句話的意思是聲明瞭一個self型別的weak指標,名字叫做weakSelf.  typeof是用來求引數型別的,這裡也就是來求self的型別。這樣定義出的weakSelf就是和self是一個型別,並且是原self的一個弱引用。

2.

__weak typeof(&*self) weakSelf = self;

3.

__weak MyViewController *weakSelf = self;


下面我通過程式碼演示一下:

(1)宣告幾個block和一個屬性:

@interface ViewController (){

    void(^myBlock1)(void);//該block引數為void,返回值為void
    void(^myBlock2)(void);
    void(^myBlock3)(void);
}

@property (nonatomic,copy) NSString *person;

@end

(2)使用weakSelf不會引起迴圈引用:
    __weak typeof(self) weakSelf = self;

    NSLog(@"init--> value:%@,address=%p,self=%p",self.person,self.person,self);

    myBlock1 = ^(void){
        //這樣不會造成迴圈引用
        NSLog(@"execute1--> value:%@,address=%p,weakSelf=%p",weakSelf.person,weakSelf.person,weakSelf);
    };


(3)直接使用self,會迴圈引用:Xcode會給警告

    myBlock2 = ^(void){
        //這樣造成迴圈引用
        NSLog(@"execute2--> value:%@,address=%p,self=%p",self.person,self.person,self);
    };


(4)要執行的方法抽取出來,也不會迴圈引用:

    myBlock3 = ^(void){
        //這樣也不會造成迴圈引用,已經抽取出要執行的方法
        [weakSelf myBlock3Func];
    };
- (void)myBlock3Func{

    NSLog(@"execute3--> value:%@,address=%p,self=%p",self.person,self.person,self);
}


(5)block不是self的屬性或者變數時,在block內使用self也不會迴圈引用:
    //block不是self的屬性時,block內部使用self也不是迴圈引用
    Animal *animal = [[Animal alloc] init];
    animal.animalBlock = ^(void){

        NSLog(@"animal--> value:%@,address=%p,self=%p",self.person,self.person,self);
    };


(6)block的呼叫如下:

    myBlock1();
    myBlock2();
    myBlock3();

    animal.animalBlock();