天天看點

iOS記憶體洩漏的常見情況

最近面試了一波,有個記憶體洩漏的問題現在回顧一下

  
首先,概念我之前有點不明确吧,面試的時候回答的有點含糊由于朋友内推去面試的也算蒙混過關了~

  


  
記憶體溢出:使用者要求系統配置設定的記憶體空間超出了系統所能提供的範圍,稱記憶體溢出

  
記憶體洩露:使用者向系統申請記憶體(new),使用完後沒有釋放的情況(delete),導緻了該塊記憶體不能再被使用者使用,也不能被系統調用。我們常說的記憶體洩露指的是堆洩露

  
記憶體越界:使用已申請的記憶體時,超出了記憶體的界限。

  
棧溢出:棧滿時再做進棧必定産生空間溢出,叫上溢,棧空時再做退棧也産生空間溢出,稱為下溢。

  
ps:典型的,在C語言中,在配置設定數組時為其配置設定的長度是1024,但往其中裝入超過1024個資料時,由于C語言不會對數組操作進行越界檢查,就會造成記憶體溢出錯誤。出現死循環或者是大量的遞歸調用,在不斷的壓棧過程中,造成棧容量超過1m而導緻溢出。

  
棧溢出解決方法

  
方法一:用棧把遞歸轉換成非遞歸

  
通常,一個函數在調用另一個函數之前,要作如下的事情:a)将實在參數,傳回位址等資訊傳遞給被調用函數儲存; b)為被調用函數的局部變量配置設定存儲區;c)将控制轉移到被調函數的入口. 從被調用函數傳回調用函數之前,也要做三件事情:a)儲存被調函數的計算結果;b)釋放被調函數的資料區;c)依照被調函數儲存的傳回位址将控制轉移到調用函數.所有的這些,不論是變量還是位址,本質上來說都是"資料",都是儲存在系統所配置設定的棧中的. 那麼自己就可以寫一個棧來存儲必要的資料,以減少系統負擔。

  
 

  
方法二:使用static對象替代nonstatic局部對象

  
在遞歸函數設計中,可以使用static對象替代nonstatic局部對象(即棧對象),這不僅可以減少每次遞歸調用和傳回時産生和釋放nonstatic對象的開銷,而且static對象還可以儲存遞歸調用的中間狀态,并且可為各個調用層所通路。

  
 

  
方法三:增大堆棧大小值

  
當建立一個線程的堆棧時,系統将會保留一個連結程式的/STACK開關指明的位址空間區域。但是,當調用CreateThread或_beginthreadex函數時,可以重載原先送出的記憶體數量。這兩個函數都有一個參數,可以用來重載原先送出給堆棧的位址空間的記憶體數量。如果設定這個參數為0,那麼系統将使用/STACK開關指明的已送出的堆棧大小值。後面将假定我們使用預設的堆棧大小值,即1MB的保留區域,每次送出一個頁面的記憶體。


  
之後,回歸主題,再說一下iOS記憶體洩漏的常見情況

  
1.聲明delegate為strong類型,簡而言之,如果父VC持有子VC,并設定子VC的delegate為self(也就是父VC),這樣的結果就是子VC也間接持有了父VC,造成循環引用,在Pop子VC的時候不會調用delloc。

  
2.timer是否持有self,我們一般要執行一個timer的時候會用(NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti  target:(id)aTarget  selector:(SEL)aSelector  userInfo:(id)userInfo  repeats:(BOOL)yesOrNo   這裡的aTarget一般是self,這時候就需要注意了,如果在你退出的時候這個timer還在執行的話由于這個timer會持有self,是以delloc也不會調用,這裡可以用weakSelf代替self也是沒有問題的。

  
3.最常見的就是block導緻的循環引用,由于在重構APP中用到了MVVM架構,使用了大量的信号機制,導緻block到處飛(哈哈),解決的最多的就是這種了,解決方法也很簡單,就是在block外聲明__weak type(self) weakSelf = self,在block中用weakSelf就可以了,還有就是在block中如果使用了成員變量的下劃線形式也要改成weakSelf.PropertyName的形式。MVVM中定義了宏對@weakify(self)和@strongify(self)可以了解為__weak type(self) weakSelf = self的簡化形式,可以拿來直接使用。

  
3.圖檔沒釋放,instrument調試後,發現沒被釋放的全是imageIO,差不多就知道了,把讀圖的方式,從[UIImage imageNamed:@""],改成imageWithContentsOfFile,就可以了。

  
4.使用GPUImage處理拍照的時候,記憶體穩定不明增長。是Xcode7.1的問題。。隻在debug的時候導緻記憶體崩潰,release的時候并不會造成記憶體溢出,是以可以不必管它。

  
5.CoreFoundation對象(C對象) : 隻要函數中包含了create\new\copy\retain等關鍵字, 那麼這些方法産生的對象, 就必須在不再使用的時候調用1次CFRelease或者其他release函數