iOS 循環參考解決方案,ios引用解決方案

來源:互聯網
上載者:User

iOS 循環參考解決方案,ios引用解決方案

一、BLOCK 循環參考

一般表現為,某個類將block作為自己的屬性變數,然後該類在block的方法體裡面又使用了該類本身。構成循環參考。

// 定義 block 的時候,會對外部變數做一次 copy,強引用, self自身為強引用。

解決方案如下:

 1 #import "ViewController.h" 2 #import "NetworkTools.h" 3  4 @interface ViewController () 5     @property (nonatomic, strong) NetworkTools *tools; 6 @end 7  8 @implementation ViewController 9 // 1. 解除循環參考,需要注意打斷引用鏈條即可!10 - (void)viewDidLoad {11     [super viewDidLoad];12     13     // 局部變數不會產生迴圈應用,全域屬性會產生循環參考14     self.tools = [[NetworkTools alloc] init];15     16     // 1. 定義 block 的時候,會對外部變數做一次 copy,會對 self 進行強引用17     18     // 解除循環參考方法119     // __weak 是 iOS 5.0 推出的20     // 如果非同步作業沒有完成,釋放控制器,__weak 本身是弱引用21     // 當非同步執行完畢,進行回調,self 已經被釋放,無法訪問屬性,也無法調用方法22     // __weak 相當於 weak,不會做強引用,但是如果對象被釋放,執行的地址,會指向 nil23     // __weak 更安全24     __weak typeof(self) weakSelf = self;25     26     // 解除循環參考方法227     // __unsafe_unretained 是 iOS 4.0 推出的28     // MRC 經典錯誤,EXC_BAD_ACCESS 壞記憶體訪問,野指標29     // 相當於 assign,不會做強引用,但是如果對象被釋放,記憶體位址保持不變,如果此時再調用,就會出現野指標訪問30     // __unsafe_unretained typeof(self) weakSelf = self;31     32     [self.tools loadData:^(NSString *html) {33         // strongSelf 強引用,對 weakSelf 進行強引用,本意,希望在非同步完成後,繼續執行回調代碼34         //然而並沒有什麼作用!!!!!!!!35         __strong typeof(self) strongSelf = weakSelf;36         37         NSLog(@"%@ %@", html, strongSelf.view);38     }];39 }40 41 - (void)dealloc {42     NSLog(@"控制器 88");43 }44 45 @end

二、計時器NSTimer循環參考

主要是因為從timer的角度,timer認為調用方self被析構時會進入dealloc,在dealloc可以順便將timer的計時停掉並且釋放記憶體;但是從self的角度,他認為timer不停止計時不析構,那我永遠沒機會進入dealloc。循環參考,互相等待,子子孫孫無窮盡也。

例子說明:

一方面,NSTimer經常會被作為某個類的成員變數,而NSTimer初始化時要指定self為target,容易造成循環參考。 另一方面,若timer一直處於validate的狀態,則其引用計數將始終大於0。先看一段NSTimer使用的例子(ARC模式):

 1 import <Foundation/Foundation.h> 2  3 interface Friend : NSObject 4     -(void)cleanTimer; 5 end 6  7 import "Friend.h" 8  9 interface Friend ()10     STimer *_timer;11 end12 13 implementation Friend14 15 -(id)init16 {17     if (self = [super init]) {18         _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(handleTimer:)19         userInfo:nil repeats:YES];20     }21     return self;22 }23 24 - (void)handleTimer:(id)sender25 {26     NSLog(@"%@ say: Hi!", [self class]);27 }28 29 - (void)cleanTimer30 {31     [_timer invalidate];32     _timer = nil;33 }34 35 - (void)dealloc36 {37     [self cleanTimer];38     NSLog(@"[Friend class] is dealloced");39 }40 41 @end

在類外部初始化一個Friend對象,並延遲5秒後將friend釋放(外部運行在非arc環境下)

1 Friend *f = [[Friend alloc] init];2 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5*NSEC_PER_SEC), dispatch_get_main_queue(), ^{3     [f release];4 });

我們所期待的結果是,初始化5秒後,f對象被release,f的dealloc方法被調用,在dealloc裡面timer失效,對象被析構。但結果卻是如此:

 1 2015-03-18 18:00:35.300 WZLCodeLibrary[41422:3390529] Friend say: Hi! 2 2015-03-18 18:00:36.299 WZLCodeLibrary[41422:3390529] Friend say: Hi! 3 2015-03-18 18:00:37.300 WZLCodeLibrary[41422:3390529] Friend say: Hi! 4 2015-03-18 18:00:38.299 WZLCodeLibrary[41422:3390529] Friend say: Hi! 5 2015-03-18 18:00:39.299 WZLCodeLibrary[41422:3390529] Friend say: Hi!//運行了5次後沒按照預想的停下來 6 2015-03-18 18:00:40.299 WZLCodeLibrary[41422:3390529] Friend say: Hi! 7 2015-03-18 18:00:41.300 WZLCodeLibrary[41422:3390529] Friend say: Hi! 8 2015-03-18 18:00:42.300 WZLCodeLibrary[41422:3390529] Friend say: Hi! 9 2015-03-18 18:00:43.299 WZLCodeLibrary[41422:3390529] Friend say: Hi!10 2015-03-18 18:00:44.300 WZLCodeLibrary[41422:3390529] Friend say: Hi!<br>.......根本停不下來.....

這是為什麼呢?主要是因為從timer的角度,timer認為調用方(Friend對象)被析構時會進入dealloc,在dealloc可以順便將timer的計時停掉並且釋放記憶體;但是從Friend的角度,他認為timer不停止計時不析構,那我永遠沒機會進入dealloc。循環參考,互相等待,子子孫孫無窮盡也。問題的癥結在於-(void)cleanTimer函數的調用時機不對,顯然不能想當然地放在調用者的dealloc中。一個比較好的解決方案是開放這個函數,讓Friend的調用者顯式地調用來清理現場。如下:

1 Friend *f = [[Friend alloc] init];2 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5*NSEC_PER_SEC), dispatch_get_main_queue(), ^{3     [f cleanTimer];4     [f release];5 });

三、委託delegate

在委託問題上出現循環參考問題已經是老生常談了,本文也不再細講,規避該問題的殺手鐧也是簡單到哭,一字訣:聲明delegate時請用assign(MRC)或者weak(ARC),千萬別手賤玩一下retain或者strong,畢竟這基本逃不掉循環參考了!

上面說的是我們常見的,其實循環參考就是說我們的強引用形成了閉環,還會有很多自己寫的代碼中會出現,平時還是要注意寫法。

不好意思,下面再囉嗦一遍,進一步說明:

循環參考,指的是多個對象相互引用時,使得引用形成一個環形,導致外部無法真正是否掉這塊環形記憶體。其實有點類似死結。舉個例子:A->B->C->....->X->B ->表示強引用,這樣的B的引用計數就是2,假如A被系統釋放了,理論上A會自動減小A所引用的資源,就是B,那麼這時候B的引用計數就變成了1,所有B無法被釋放,然而A已經被釋放了,所有B的記憶體部分就肯定無法再釋放再重新利用這部分記憶體空間了,導致記憶體流失。情況一:delegateDelegate是ios中開發中最常遇到的循環參考,一般在聲明delegate的時候都要使用弱引用weak或者assign@property (nonatomic, weak, nullable) id <UITableViewDelegate> delegate;當然怎麼選擇使用assign還是weak,MRC的話只能用assign,在ARC的情況下最好使用weak,因為weak修飾的變數在是否後自動為指向nil,防止不安全的野指標存在情況二:BlockBlock也是比較常見的循環參考問題,在Block中使用了self容易出現循環參考,因此很多人在使用block的時候,加入裡面有用到self的操作都會聲明一個__weak來修飾self。其實便不是這樣的,不是所有使用了Block都會出現Self循環參考問題,只有self擁有Block的強引用才會出現這種情況。所以一般在函數中臨時使用Block是不會出現迴圈應用的,因為這時候Block引用是屬於棧的。當棧上的block釋放後,block中對self的引用計數也會減掉當然不一定要Self對Block有直接的引用才會出現,假如self的變數B,B中有個Block變數,就容易出現這種情況,好的是在block出現循環參考的,xcode7會出現警告提示(之前版本不確定)。情況三:NSTimer這是一個神奇的NSTimer,當你建立使用NSTimer的時候,NSTimer會預設對當前self有個強引用,所有在self使用完成打算是否的時候,一定要先使用NSTimer的invalidate來停止是否時間控制對self的引用[_timer invalidate];

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.