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();