1. 程式人生 > >ARC下的記憶體管理

ARC下的記憶體管理


  • ARC是誰,它能幹嘛?

    ARC全稱叫Automatic Reference Counting。簡單地說,就是程式碼中自動加入了retain/release,原先需要手動新增的用來處理記憶體管理的引用計數的程式碼可以自動地由編譯器完成了。簡單地理解ARC,就是通過指定的語法,讓編譯器(LLVM 3.0)在編譯程式碼時,自動生成例項的引用計數管理部分程式碼。有一點,ARC並不是GC,它只是一種程式碼靜態分析(Static Analyzer)工具[1]。

    之前聽ARC開發者說過一句話:“哥們,你要知道我是全職做這個事(ARC)的,你還擔心什麼”。我覺得大多數人自己用MRC的方式對記憶體的管理能顯然無法超過Apple裡面開發ARC的人對記憶體的管理水準,所以放心的用ARC吧。     但是ARC的管理許可權是有限的,僅限於對於NSObject類物件的管理。而我們在IOS開發中會用到的Core Foundation型別(CFType)的物件ARC就鞭長莫及了。
  •  堆與棧
    我們首先了解堆與棧的概念     ------"棧“者,儲存貨物或供旅客住宿的地方,可引申為倉庫、中轉站(FILO)。     棧在程式的執行中有著舉足輕重的作用。最重要的是棧儲存了一個函式呼叫時所需要的維護資訊,這常常稱之為堆疊幀或者活動記錄。堆疊幀一般包含如下幾方面的資訊:     1.函式的返回地址和引數     2.臨時變數:包括函式的非靜態區域性變數以及編譯器自動生成的其他臨時變數。 程式會將正在執行的函式入棧,執行完畢之後將此函數出棧。     ------堆就是堆     堆是程式設計師申請的一塊連續的記憶體,如使用malloc()申請一塊記憶體緩衝區,當不再使用這塊緩衝區的時候,可以呼叫free()函式釋放相應的記憶體,將其返回給堆。
    現在我來舉個“栗子”:     -(void)fun{        @autoreleasepool{             NSData *time = [NSData data];         }     }     這是一個oc的函式,如果我們呼叫這個函式,則中就會push進這個函式幀,此函式幀中包含了一個區域性變數time,這是一個指標型別的變數,變數前面的*說明它是一個指標(其實在oc裡面指標指向的是一個結構體,在此提一下不深究),這個指標儲存了NSData例項在記憶體中的地址。 而賦值運算子右邊的 [NSData data]返回了一個地址賦值給time,這個地址所表示的記憶體塊儲存在
中。 由於我們申請了這塊記憶體,所以我們需要手動的釋放他,所以在函式中寫了@autoreleasepool{},在@autoreleasepool{}執行完之後這塊記憶體就釋放掉了。 這是在MRC的情況下一種釋放記憶體的方法,而在ARC中對於NSObject型別的物件系統會自動進行管理,我們只要寫成:
    -(void)fun{             NSData *time = [NSData data];      } 就可以了,執行完fun函式之後堆中的記憶體會由ARC管理釋放掉。 我們可以使用NSLog(@"%p", time); 列印這塊記憶體的地址;使用NSLog(@"%@", time);列印記憶體中的內容。     我們所討論的記憶體管理其實是在討論如何管理堆,棧中的幀在呼叫完之後會自動釋放,而堆是不會自動釋放的。
  • ARC出山
    當我們在xcode上使用oc寫程式的時候,系統會為每一個例項物件進行引用計數,引用是指標的另外一種叫法,說白了就是計算有多少指標指向它(只看強引用)。當一個例項物件沒有指標指向它的時候,ARC就出來把這塊記憶體回收了。     呼叫free()函式來銷燬物件是清理的最乾淨的方法。使用ARC,只能通過增加或減少對兌現搞的引用數來保留或者銷燬物件。當引用數為0的時候,他才會被銷燬。     如果你已經用xcode寫過oc程式了,那麼你回憶一下你是如何新建一個類的。當我們要新建一個自定義的類的時候我們會在xcode介面的左側專案檔案區域右鍵滑鼠,選擇new File...,然後在彈出的對話方塊中選擇Source,這時候重點來了,你接下來選擇的會是一個叫“Cocoa Touch Class”的東西。是的,這就意味著你新建了一個cocoa類,當新建完成之後我們進入新建類的h檔案就可以看到,它是繼承了NSObject的。之前我提到過,ARC會管理好NSObject型別,但是不會管理CF型別。但是,這並不意味著我們可以對所有的NSObject型別高枕無憂了,你用的不好照樣會出現記憶體洩漏^_^。因為,有一種情況叫做強引用迴圈。              ---- 強迴圈引用       什麼叫做迴圈引用呢,現在我來舉個“栗子”: 現在有一個類叫做Father ,另外還有一個類叫做Son,均為繼承於NSObject。 Father Class: @interface Father:NSObject @property(nonatomic,strong)Son *son; @end Son Class: @interface Son:NSObject @property(nonatomic,strong)Father *father; @end int main(){     Father *f = [[Father alloc] init];     Son *s = [[Son alloc] init];     f.son = s;     s.father = f; } 我們看到兩個property均設為了strong,也就是強引用,而現在s,f兩個物件事實上是相互引用的,這樣的話ARC是無法釋放兩個物件的。這種情況就是我們在ARC情況下應該避免的強迴圈引用。關於如何xcode下如何檢測記憶體洩漏可以看下這篇文章[2]。             那麼我們應該如何避免強迴圈引用呢?說起來很簡單,把強迴圈引用改成弱迴圈引用就行了啊!對於上面的程式碼,我們應該將Son Class中的Father屬性改為弱引用:@property(nonatomic,weak)Father *father;              如果物件間是父子關係,那麼為了避免強迴圈引用,通常需要遵守此規則:父物件擁有子物件,但是子物件不能擁有父物件             一般來說,我們對於物件屬性的修飾會遵循 [3]:             (1)strong還是weak
              說到底就是一個歸屬權的問題。小心出現迴圈引用導致記憶體無法釋放,或者需要引用的物件過早被釋放。大體上:IBOutlet可以為weak,NSString為copy或strong,Delegate一般為weak,基礎型別用assign,不過要注意具體使用情況。
            (2)outlet使用strong還是weak
             官方文件建議一般outlet屬性都推薦使用weak,不是直接作為main view裡面一個subview直接顯示出來,而是需要通過例項化創建出來的view,應該使用 strong(自己建立的自己當然要保持引用了)。但是要注意使用 weak時不要丟失物件的所有權,否則應該使用strong。
            (3)delegate使用strong還是weak
             delegate主要涉及到互相引用和crash(引用被釋放)問題,為了防止這兩個問題發生,delegate一般使用weak。
  • ARC退下
      前面說了ARC管不了CFTypes的物件,所以當遇到Core Foundation時,ARC就可以退下了。 Core Foundation 物件必須使用CFRetainCFRelease來進行記憶體管理。那麼當使用Objective-C 和 Core Foundation 物件相互轉換的時候,必須讓編譯器知道,到底由誰來負責釋放物件,是否交給ARC處理。只有正確的處理,才能避免記憶體洩漏和double free導致程式崩潰。         根據不同的需求,有三種轉換方式:        (1)__bridge 不改變物件所有權             比如:NSDictionary *option = [......];(這是一個NSObject物件),                       CFDictionaryRef option_dic = (__bridgeCFDictionaryRef)option;                       這時候我們不用寫CFRelease(option_dic),因為它不具有option的所有權,option還是歸ARC管,不關你的事。        (2)__bridge__retained  解除ARC所有權,也就是說這個物件由我自己來管理        (3)__bridge__transfer   給予ARC所有權         在實際的寫程式過程中我遇到過overrelease的情況,就是重複釋放,CFRelease()不能傳入一個已經dealloc的物件。所以我們應該先搞清楚各個CFTypes之間的依賴關係,並且在寫CFrelease的時候最好寫成 if(x != null){  CFRelease(x); x = null;  }。 關於overrelease可以看這個很簡單的小例子[4]感受一下。 看完栗子[4]之後講一下我自己實際遇到的問題: 以上的程式碼中注意一下 CGImageRef imageRef = CGimageSourceCreateImageAtIndex(source,0,option_dict); 我在之後的程式碼中寫了一下的釋放語句: 是的,自信點選command+R執行程式,直接在CFRelease(source)那裡掛掉(-。-) 網上的人說只要看到類似於CGimageSourceCreateImageAtIndex這樣的方法中有create關鍵字就應該進行CFRelease,我遵循了。但是實際執行的時候發現是overrelease。後來我去看CGimageSourceCreateImageAtIndex這個方法的API,裡面的註釋寫到: 這個應該是返回了一個指標吧,而且看source建立的程式碼source中只放有一張圖片,也就是說source其實應該是image的容器,很有可能是這樣的情況: 如果沒推測錯的話這個情形和[4]中遇到的情況是完全一樣的道理。所以我那樣的釋放記憶體方式才會造成overRelease。 最後關於free()的內容,可以看下[5]參考下。關於記憶體的操作還可以看下這篇文章[6][7][8] 這篇文章寫得還是非常淺的,說的不對的地方一起討論。 Reference:

相關推薦

ARC記憶體管理

單個物件ARC下的記憶體管理 我們首先介紹建立物件時的記憶體分配: 1.分配記憶體空間,儲存物件 2.初始化成員變數 3.返回物件的指標地址 在物件建立完成的同時,內部會自動建立一個引用計數器,值得注意的是,物件內部的計數器是判斷是否回收物件的唯一依據

iOS ARC記憶體管理問題以及解決辦法

很多同學因為沒有經歷過使用手動引用計數來管理記憶體,一直在ARC下愉快的開發導致對iOS記憶體管理方式的不理解,巧哥有篇專門說明iOS記憶體管理,我也是記錄學習中的點滴,共勉之。 我剛接觸iOS的時候ARC已經出現,但是大家對這種黑科技都保持懷疑態度,大部分人還是在使用手動

ARC記憶體管理

 ARC是誰,它能幹嘛?     ARC全稱叫Automatic Reference Counting。簡單地說,就是程式碼中自動加入了retain/release,原先需要手動新增的用來處理記憶體管理的引用計數的程式碼可以自動地由編譯器完成了。簡單地理解ARC,就

記憶體管理

五、實體記憶體的管理 在核心初始化完成後,記憶體管理的責任由夥伴系統(高效、高速)承擔。 1、夥伴系統的結構 系統記憶體中的每個實體記憶體頁(頁幀),都對應於一個struct page例項。每個記憶體域都關聯了一個struct zone的例項,其中儲存了用於管理夥伴資料的主要陣列。 1 stru

IA-32e架構的核心初始化記憶體管理

初級記憶體管理單元 關於記憶體的分頁 以往的物理頁是按照4KB進行分配和管理的, 而在Linux之後流行的就是2MB大小的物理頁的分配和管理, 整個實體記憶體管理單元也是2MB物理頁管理的 先獲取基本的實體地址空間資訊 在bootloader程式中, 已經呼叫了BIOS的int 15h

Java記憶體管理記憶體洩露是什麼?什麼情況會導致記憶體洩露?

文章目錄 1. 靜態類的使用 2. 資源連線的使用 3. 監聽器的使用 雖然Java擁有垃圾回收機制,但同樣會出現記憶體洩露問題,我們說一下比較主要的三種情況。 1. 靜態類的使用 諸如 HashMap、Vector 等集

OC知識--徹底理解記憶體管理(MRC、ARC)

1. 什麼是記憶體管理 程式在執行的過程中通常通過以下行為,來增加程式的的記憶體佔用 建立一個OC物件 定義一個變數 呼叫一個函式或者方法 而一個移動裝置的記憶體是有限的,每個軟體所能佔用的記憶體也是有

Linux程序記憶體管理之malloc和sbrk

之前自己突發興趣想寫一下malloc函式,順便了解一下程序的記憶體管理。在寫的過程中發現其實malloc只不過是通過呼叫Linux下的sbrk函式來實現記憶體的分配,只是在sbrk之上加了一層對所分配的記憶體的管理罷了,而sbrk以及brk是實現從虛擬記憶體到記憶體的對映的

iOS OC記憶體管理ARC、property屬性、__strong、__weak、__block——iOS 編碼複習(一)

首先來聊聊記憶體管理。因為是先有了記憶體管理這個東西,才慢慢的有了ARC,而後才會有@property的各種屬性 聊到記憶體管理,我們就能知道iOS5之前,iOS的記憶體管理是MRC(手動記憶體管理)的。iOS5之後才有了ARC(自動記憶體管理)。 那我們就來看看MRC是怎

作業系統核心原理-5.記憶體管理):段式記憶體管理

一、分頁系統的缺點   分頁系統存在的一個無法容忍,同時也是分頁系統無法解決的一個缺點就是:一個程序只能佔有一個虛擬地址空間。在此種限制下,一個程式的大小至多隻能和虛擬空間一樣大,其所有內容都必須從這個共同的虛擬空間內分配。 二、分段管理系統 2.1 何為分段管理   分段管理就是將一個程式按照邏輯單

淺析Linux的堆記憶體管理

  最近在看一本叫《程式設計師的自我修養-連結.裝載域庫》(俞甲子,石凡,潘愛民 著)這本書,不得不像大家安利這本書,從最基本的EFL檔案到連結,再到裝載,雖然現在還沒看完,但是對程式的可執行檔案和虛擬記憶體的佈局有了進一步的瞭解,不得不說是一本好書。言歸正傳,本文討論的話

ARC記憶體洩漏

ARC全稱叫 ARC(Automatic Reference Counting)。在編譯期間,編譯器會判斷物件的使用情況,並適當的加上retain和release,使得物件的記憶體被合理的管理。所以,從本質上說ARC和MRC在本質上是一樣的,都是通過引用計數的記憶體管理方式。ARC 的出現大大節省了程式設計

Linux堆記憶體管理深入分析(

Linux堆記憶體管理深入分析 (下半部) 作者@走位,阿里聚安全 0 前言回顧 在上一篇文章中,詳細介紹了堆記憶體管理中涉及到的基本概念以及相互關係,同時也著重介紹了堆中chunk分配和釋放策略中使用到的隱式連結串列技術。通過前面的介紹,我們知道使用隱式連結串

R語言︱大資料集執行記憶體管理

如果建立一個filebacked.big.matrix,那麼需要指定backingfile的名稱和路徑+descriptorfile。可能多個big.matrix物件對應唯一一個descriptorfile,即如果descriptorfile改變,所以對應的big.matrix隨之改變;同樣,decripto

iOS ARC 記憶體管理要點

前言 在討論 ARC 之前,我們需要知道 Objective-C 採用的是引用計數式的記憶體管理方式,這一方式的特點是: 自己生成的物件自己持有。比如:NSObject * __strong object = [NSObject alloc] init];。非自己生成

Linuxglibc記憶體管理

整理的參考文獻,記不清了 1 背景簡介 出現疑似”記憶體洩露”問題: malloc申請的記憶體,free釋放以後沒有歸還作業系統,比如記憶體模組佔用的記憶體為10GB,釋放記憶體以後,通過TOP命令或者/proc/pid/status檢視佔用的記憶體有時仍然為10G,

記憶體管理ARC

assign:指定setter方法用簡單的賦值,這是預設操作。你可以對標量型別(如int)使用這個屬性。你可以想象一個float,它不是一個物件,所以它不能retain、copy。 retain:指定retain應該在後面的物件上呼叫,前一個值傳送一條release訊息。你可以想象一個NSStr

記憶體洩露之自動記憶體管理(ARC)

一.單個物件的記憶體管理        在Xcode5.0之前都是手動記憶體管理的,但是在5.0之後就不需要程式猿管理了,為什麼呢?因為Xcode會自動在我們需要釋放的地方加上release,這也是Xcode的一項功能吧.這樣就能免去了我們對程式碼的花太多的時間關心記憶體釋

IOS記憶體管理,ARC,MRC,自動釋放池(基礎)

在IOS中記憶體管理幾乎是每個人必須知道的一個知識點。首先我們總結一下MRC,再通過MRC來認識ARC以及自動釋放池 1.MRC 1.1 淘汰的技術 1.2 引用計數(RC)是指alloc自動分配的一塊兒儲存空間,用於儲存持有該空間的指標個數 1.3 使

純DOS記憶體管理—真實模式訪問4GB記憶體

好了,廢話說了這麼多,再不切入正題的話估計會有人向我扔雞蛋了,下面就來告訴大家怎麼做到在真實模式下訪問4GB記憶體。這種技術需要保護模式支援,所以只能在80386以上的CPU中執行。 學過一點保護模式的讀者都知道,在保護模式下段地址暫存器中內容的不再象真實模式那樣是段的基地址,而只是描述符表中的一個索引,段的