iOS內存管理機制及屬性的使用 – iPhone手機開發 iPhone軟體開發教學課程

對於OC的使用者來說,最會被問到的就是iOS開發中的內存管理。而隻要涉及到內存管理,就肯定會涉及到property。而且在平常iOS開發的面試中,我們也經常會被問到相關的問題。所以這次就結合我所見到的和自己對於屬性的理解來進行簡述,希望對你們有幫助。

在講述屬性前,我們需要對於iOS開發中的內存管理有一個初步的瞭解。

iOS開發中的內存使用情況

棧(stack):棧是編譯器自動分配並釋放,用來存放函數的參數,局部變量。 堆(heap):堆一般是程序員自己分配和釋放,如果我們在使用的過程中,沒有釋放,那麼等到程序完全結束,系統將會對堆中的內容進行回收。一般開發中的alloc就是存放在堆中的 全局變量(靜態變量)(static):全局變量和靜態變量是單獨存放的,因為他們的聲明周期和整個程序的生命周期一致。釋放的時間是由整個程序結束後系統負責回收。 文字常量區:一般用來存放常量字符串,比如說String *str = @"hello world",他的釋放也和全局變量一致,在程序結束後進行釋放。 程序代碼區:用來存放函數的二進制內容的區域。

他們之間的在內存中存放的關系如下圖所示:

而在日常開發過程中,我們所遇到最多的也是最容易問到的就是,可能有的人對於這兩者還是很模糊,所以我再引用之前看到過的一段話來解釋(由於很久以前看到瞭,已經忘記瞭出處,如果有人能夠告訴我,我一定加上):

內管管理就像做菜,而的使用就像我們下館子,我們隻要去酒店,告訴老板我們要什麼,他們就會幫我們準備好,我們隻要吃完付錢,就可以瞭,也不用去管理那些之後的瑣事。而就像我們自己做菜,什麼東西都得自己買好,做什麼,怎麼做都得提前想好。做完之後還需要自己收拾最後的垃圾。

iOS內存管理機制

當你創建的對象的時候,你總得需要告訴操作系統,你將要在什麼時候對占有這塊內存的對象進行釋放,就像平常生活中,你怎麼才能確定這房間裡面是有人的還是沒有人的呢?

一般來說,我們肯定會這麼幹,每個人進去的時候登記下,然後出來的時候再登記下,這樣隻要我們在需要管理的時候看下登記再按的人數是不是為0就可以瞭,隻要為0,那麼就說明裡面沒有人,否則就有人,蘋果也是這麼處理內存管理的。在你創建一個對象的時候,他會需要進行retain,然後在你不在持有他的時候進行release,所以每個對象都有一個retain count來進行計數。

在iOS中有2套內存管理機制:MRC(MannulReference Counting)和ARC(Automatic Reference Counting)。其中ARC起源於iOS 4.3,在那之前,蘋果開發者隻能手動使用retain和release來進行內存管理。這樣做的問題很明顯,如果你在開發過程中,一個不小心沒有retain和release成對出現,那麼很容易使得內存沒有釋放,最後導致程序內存不足而導致閃退。

對於ARC,蘋果的官方文檔是這麼解釋的:

Automatic Reference Counting (ARC) is a compiler feature that provides automatic memory management of Objective-C objects. Rather than having to think about retain and release operations, ARC allows you to concentrate on the interesting code, the object graphs, and the relationships between objects in your application.

ARC works by adding code at compile time to ensure that objects live as long as necessary, but no longer. Conceptually, it follows the same memory management conventions as manual reference counting (described in Advanced Memory Management Programming Guide) by adding the appropriate memory management calls for you.

總的來說,主要意思就是ARC在本質上就像MRC一樣,但是它能夠讓你花費更少的時間來考慮代碼中的retain和release。與此同時,編譯器能夠幫你更加準確的將retain和release加在你代碼中真正需要的地方。

理解如下圖:
ARC && MRC

有人可能會提到GC(Garbage Collection)——垃圾回收機制。在現在的iOS中GC沒有被使用,而在MAC OS X中,GC是被使用的,不過對於GC,蘋果是這麼解釋的:

Garbage collection is deprecated in OS X Mountain Lion v10.8, and will be removed in a future version of OS X. Automatic Reference Counting is the recommended replacement technology. To aid in migrating existing applications, the ARC migration tool in Xcode 4.3 and later supports migration of garbage collected OS X applications to ARC

總的來說ARC將在未來的某一天來取代GC在MAC中的位置。

property(屬性)

就算理解的再多,我們還是要和實際相結合。實際運用中與之相對應的就是實例變量的屬性。
由於 iOS 中 MRC已經太過於古老,在這就不再多提。

在ARC中,我們創建實例變量經常是這樣的

@property (atomic/nonatomic/assign/retain/strong/weak/unsafe_unretained/copy) Number* num

所以我們就為讓這內容中的這幾個property來進行解釋。

atomic:原子性,簡單的解釋就是說,他是線程安全的,但是由於線程安全,在操作的過程中,編譯器會自己給他上鎖,解鎖。這樣會造成資源的浪費(因為我們平常開發中不會那麼頻繁的考慮到線程安全這個問題)該屬性為默認值 nonatomic:非原子性,這個和上面那個是雙子星,但是他們正好相反,這個由於不安全的,但是因為沒有瞭鎖的問題,這樣資源就會盡可能的被利用,所以我們在日常開發中使用這個,而對於他的安全性,我們一般都自己在後期開發的過程中,在使用多線程的過程中進行考慮。 assign:給予瞭setter方法,assign一般指向一個不是指針指向的對象,比如說CGFloat這類值。(關於assign和weak在delegate中的區別將會在後面提到) retain:retain用於指向一個有指針的對象,就像MRC中的retain,他是為瞭增加retain count,這樣使得對象能夠在autoreleasepool中持有內存 strong:strong是在ARC中用來代替retain的,所以原理相同,即retain count加一 weak:weak和strong一般隻要懂一點英語的就知道,他們又是一堆雙子星,strong是表示持有,而weak表示,隻是簡單的引用。這意味著等到持有weak的對象唄釋放的時候,weak表示的對象也被釋放。而需要註意的是weak釋放後他就會為nil。 unsafe_unretained:unsafe_unretained一般很少被用到,主要的原因是他一般用於在Cocoa底層的那些不能支持weak屬性的變量,比如說NSTextView,NSFont ,NSColorSpace等。而他和weak不同的地方是,持有weak的對象被釋放後,weak對象會被指向nil,而unsafe_unretained則不會被指向為nil。這樣就有瞭安全隱患。 copy:copy一般我們用在NSString,NSArray,NSDictionary,而原因就是copy會在復制的時候講源對象進行拷貝。這樣他隻想的將會是一個新的,retain count為1的對象。這樣在我們對於這個對象的內容進行修改的時候,它將不會影響到原來的那個對象。

除瞭這幾種之外,我們還有4種屬性沒有提到
– getter:設置getter方法
– setter:設置setter方法
– readonly:隻讀
– readwrite:可讀寫

這四種因為可以根據字面意思來進行設置在這就不進行過多的介紹。

這裡再補上一句之前提到的問題:

關於為什麼使用weak而不是用assign來對delegate進行標註。

首先delegate一般的類型都是id,即可以指向所有對象的id類型。所以我們既可以使用assign指向他,也可以使用weak指向他。但是因為weak在不被持有的時候會指向nil,而大傢都知道,所有通過nil的方法在調用函數的時候,都會返回為nil,這樣就能夠保證瞭程序的穩定性,就算沒有東西返回,他還是能夠正常解析(隻是解析出來的值為nil)。而如果使用assign的話,如果一不持有他。那麼下次再調用它的時候,他將會指向一個不知名的地址,即野指針。這樣就會使得整個程序crash。

autoreleasepool(自動釋放池)

既然扯到release,那就不得不提下autoreleasepool,顧名思義,他就是一個用來自動幫你釋放的池子(這特麼不是廢話麼)。一般情況下我們不怎麼會去使用它,因為在AppKit和UIKit的框架中,事情基本上默認的放在autorelease pool block中完成的。這樣子當你完成這些內容後,對應在內存中的數據就會自動釋放,這樣就不需要你手動去處理這些數據,從而保證瞭程序的安全性。

當然這裡也提到瞭這是在使用AppKit和UIKit的情況下。我們仍然有以下這幾種情況來使用autoreleasepool的情況:

編寫的是命令行程序,不基於UI框架 當你需要寫一個循環,循環裡面有很多臨時變量的時候 當你大量使用輔助線程

總的來說autoreleasepool是為瞭盡可能的減少無用變量在內存中的占用情況。從而使得程序所需要的內存更少。至於autoreleasepool的釋放時間,這就要涉及到runloop,相信對你肯定有很大幫助。

循環引用

循環引用對於內存來說是一個大問題。這個問題我們更多的在block中可能會碰到,不過總結起來就是這麼一句話:

A對象持有B,那麼B的retain count 為1,而此時A因為被其他變量持有,所有A的retain Count為1,而B又要引用A,那麼A的retain count又要加一,這樣等到持有A的那個對象釋放A的時候,就算A的retain count減一,但還是為1,所以無法釋放,而因為A無法釋放,導致B也無法成功釋放,而外部沒有持有A或B中的任意一個,這樣就導致瞭這塊內存空間一直被持有。

具體的問題我將會在解釋block的時候具體說明,需要知道的一點就是,如果存在循環引用,那麼就需要把循環引用中間的一條線斷掉,從而使用weak來代替,使得當strong的一方被釋放的時候,weak也能被正常釋放。

希望以上文章能夠對你有所幫助。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *