1. 程式人生 > >如何防止迴圈引用 assign

如何防止迴圈引用 assign

首先說下什麼是迴圈引用,迴圈引用是指兩個物件互相retain對方,通過OBJC的release是無法銷燬這兩個物件的。更嚴重的是,如果幾個物件間接相互引用,比如A<-B,B<-C,C<-A,那麼A、B、C都無法通過release釋放。
例如下面的引用關係:
• 物件a建立並引用到了物件b.
• 物件b建立並引用到了物件c.
• 物件c建立並引用到了物件b.
這時候b和c的引用計數分別是2和1。當a不再使用b,呼叫release釋放對b的所有權,因為c還引用了b,所以b的引用計數為1,b不會被釋放。b不釋放,c的引用計數就是1,c也不會被釋放。從此,b和c永遠留在記憶體中。
這 種情況,必須打斷迴圈引用,通過其他規則來維護引用關係。

引用鏈條中只要有一環發生迴圈引用,就會導致記憶體洩露。工具難以查出,因為符合使用約定。

遺憾的是相互引用十分常見,對於介面來說有時是必要的,特別是OBJC大量使用委託模式進行回撥。因此OBJC也建議委託最好不進行retain,而改用 assign,以避免迴圈引用。不過一些非同步呼叫,多執行緒使用是很難通過此種方式解決的,容易出現野指標。所以要解決迴圈引用導致的記憶體洩露需要從根源理 解洩露出現的原因,而不是單純禁止使用。

迴圈引用問題,是引用計數所引入的問題。因此使用引用計數作為資料管理的架構都會出現類似問題。JAVA、C#的垃圾回收機制都有專門的演算法來解決此問 題。引數管理的資料都是共享關係,相互引用就是相互共享,這種記憶體洩露是符合共享邏輯的。但實際情況中,當出現相互引用,往往存在依賴關係。一個引用為擁 有,而另一個僅為回撥。回撥引用依賴擁有引用。引數管理不能直接描述依賴關係,因此需要手工打斷。

現假設B處由於某種原因使C被釋放掉,比如執行緒結束或是Cancel操作,則此時在由root處釋放A時,則全部釋放。

所以不是隻要迴圈引用就會記憶體洩露,通過打斷解決記憶體洩露是有效的,因為打斷補充了相互引用的依賴邏輯。但要注意,如果出於省事僅將打斷程式碼放置在dealloc中,則仍然會出現記憶體洩露。原 因在於release字面意思是釋放的意思,但這裡的釋放不是記憶體的最終釋放而是所有權的釋放。呼叫release是分不清記憶體釋放還是僅僅減了一個計 數。迴圈引用時,存在於dealloc中的打斷程式碼,將永遠不會通過release呼叫。因此要解決迴圈引用,還要注意不要將打斷程式碼放置在 dealloc中,而要在使用完畢後立即打斷,dealloc的打斷只能作為象徵性防護。

總結:

防止迴圈引用導致的記憶體洩露,基本上有兩種方法。

1)如果其中一個物件的生命週期覆蓋另一個物件,則採用OBJC推薦的策略,短週期物件保持長週期物件使用assign。

2)如果相互引用的物件其生命週期不能覆蓋對方,則必須在各自生命週期的終點處打斷對方,打斷程式碼不能依賴在dealloc時打斷。比如對於UI物件可通過事件處打斷,執行緒物件於Main結尾處打斷。只要有一處打斷被執行就不會產生記憶體洩露。

此外還有另一種解決方法——利用指標的指標建立依賴引用機制。

這裡提供一下思路:引用雙方只assign不retain,雙方再額外儲存對方引用的指標,當一方release時順便將自身在對方的引用清空,以達到通知連線斷開的目的。可將這種引用關係封裝為一個類簡化使用。

優點:避免迴圈引用導致記憶體洩露,即便在release處釋放連線也不會引起記憶體洩露。

缺點:引用返回的是id型別,因此使用時需要型別轉換。

另外程式設計時還要注意非預期的迴圈引用。比如OBJC庫中經常使用字典作為引數的儲存位置,而字典使用retain儲存物件。如果A保持字典,字典中又保持A,或者保持B,但B保持A,則有可能產生記憶體洩露。

總之迴圈引用引起記憶體洩露,是需要在OBJC中額外注意的。C/C++不會產生此問題,因為引用需要手工維護。JAVA、C#中由於垃圾回收機制,也無需刻意注意。

相關推薦

如何防止迴圈引用 assign

首先說下什麼是迴圈引用,迴圈引用是指兩個物件互相retain對方,通過OBJC的release是無法銷燬這兩個物件的。更嚴重的是,如果幾個物件間接相互引用,比如A<-B,B<-C,C<-A,那麼A、B、C都無法通過release釋放。例如下面的引用關係:•

block使用小結、在arc中使用block、如何防止迴圈引用

使用block已經有一段時間了,感覺自己瞭解的還行,但是幾天前看到CocoaChina上一個關於block的小測試主題 : 【小測試】你真的知道blocks在Objective-C中是怎麼工作的嗎?,發現竟然做錯了幾道, 才知道自己想當然的理解是錯誤的,所以抽時間學習了下,並且通過一些測試程式碼進行測試

block使用時的一些情況以及防止迴圈引用

block 是在 iOS 4 中引入的新特性,蘋果官方推薦使用這種做法。 block 注意事項 1,block 在實現時就會對它引用到的它所在方法中定義的棧變數進行一次只讀拷貝,然後在 block 塊內使用該只讀拷貝。 如下程式碼: -(void)tes

探討OC的記憶體管理 以及防止迴圈引用retain cycle 代理為什麼用weak block為什麼用copy

首先宣告OC的記憶體管理機制:引用計數。 當一個物件的引用計數為0時,這個物件就會被釋放。 銘記以上兩點,我們開始來探討。 換句話說,如果一個物件,沒有強指標指向,就會被釋放。 舉個很形象的例子,假如每一個物件都是一隻狗,每一個強指標都是狗鏈子

iOS 防止迴圈引用的兩個方法

__block UITableViewController *weakself = self; [self.tableView addJElasticPullToRefreshViewWithActionHandler:^{

運用block,防止迴圈引用

例如: [self.viewModel.titleSignal subscribeNext:^(NSString * title) { self.title = title; }]; 有迴圈引用的問題:self->viewM

防止Block的迴圈引用(技巧)

技巧1: __weak typeof(self)weakSelf=self; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_

防止Block迴圈引用

防止block的迴圈引用時,使用__weak關鍵字做如下定義: __weak typeof(self) weakSelf = self; 在專案中多次用到時, 可以定義一個巨集,不是每次都要寫這一行程式碼 #define WeakSelf __weak typeof

swift-防止閉包迴圈引用的方法

迴圈引用場景描述有兩個類HttpTool類和ViewController類,ViewController有HttpTool屬性,HttpTool中有callBack型別的閉包做屬性;即ViewController引用了HttpTool例項,HttpTool裡引用了callBa

使用 GC、Objgraph 幹掉 Python 記憶體洩露與迴圈引用

Python使用引用計數和垃圾回收來做記憶體管理,前面也寫過一遍文章《Python記憶體優化》,介紹了在python中,如何profile記憶體使用情況,並做出相應的優化。本文介紹兩個更致命的問題:記憶體洩露與迴圈引用。記憶體洩露是讓所有程式設計師都聞風喪膽的問題,輕則導致程式執行速度減慢,重則導致

iOS 不使用 Weak-Strong Dance,怎麼避免迴圈引用

疑惑 以下是引用: 這是來自 PopCustomAnimation.h /** @param target The object being animated. Reference the passed in target to help avoid retain loo

Spring 迴圈引用解決方案

Spring 迴圈引用解決方案 一、問題呈現:   上述memberMerchantService 和 memberService 互相引用或者深層注入引用 導致專案啟動不了 【詳細問題描述】: Bean with name ‘xxxS

Spring 迴圈引用 ——理解singleton與prototype初始化的區別

所謂的迴圈引用,就是A依賴B,B又依賴A,A與B兩個物件相互持有。像下面這種情況:   class A { B b; public A(B b) { this.b=b; } } class B { A a; public B(A a ) { this.a=a

淺談OC中的迴圈引用問題

將近一個月沒有寫文章了,藉口就不一一羅列了。。。。 廢話不多說,進入正題。 談到迴圈引用這個問題,相信很多iOS的童鞋至少都在運用block技術的時候遇到過,同樣的,很多童鞋肯定也是通過weak這個關鍵字來處理的,但是我相信,這其中肯定有不少童鞋並沒有搞明白為什麼會發生迴圈引用。本篇短文章,就以自己的理解

防止變數引用丟失 FormerlySerializedAs

一般的用法:重新命名某public或[Serialized]的欄位後,會導致引用丟失,在重新命名之前加上該標籤儲存引用則可以避免。 防止重新命名變數後丟失引用 using UnityEngine; using UnityEngine.Serialization; public

智慧指標的死穴 -- 迴圈引用

C++最新標準C++11中已將基於引用計數的智慧指標share_prt收入囊中,智慧指標的使用門檻越來越低,不需要使用boost庫,我們也能輕鬆享受智慧指標給我們帶來的方便。 智慧指標,正如它的名字一樣,似乎是個近乎完美的聰明角色,程式設計師不用再糾結於new出來的記憶體在哪釋放比較合適

swift裡迴圈引用問題

在參加面試的時候經常會被問得一個問題就是“你在工作中有沒有遇到迴圈引用的情況?” 首先我們先看段程式碼 var newSuccessCb: (() -> ())? override func viewDidLoad() { super.viewDidLoa

vue 解決迴圈引用元件報錯的問題

問題由來 最近在做專案的時候遇到使用迴圈元件,因為模式一樣,只有資料不一樣。按照普通元件呼叫格式來做的時候總是報錯,錯誤資訊為[Vue warn]: Unknown custom element: <selfile> - did you register

迴圈引用導致的json序列化失敗

問題 昨天在給系統加日誌後,系統就一直報 Stack Overflow錯誤,找了很久才發現問題,引入的日誌工具使用 gson序列化,而列印的日誌物件裡包含迴圈引用,導致出錯。 簡單復現 /** * C

[C++11] 迴圈引用

前言 雖然C++11中的智慧指標,一定程度上簡化了C++當中的記憶體管理;但是,shared_ptr<>的使用同時也引出了另一個問題:迴圈引用。 例子 讓我們先來看一段示例程式碼。 #include <iostream> #include