瞭解Cocoa中記憶體如何管理是本文要介紹的內容,不多說,直接進入話題。今天我們來談論Cocoa中關於記憶體管理的問題。這個問題無論是對於案頭開發還是移動開發都非常重要。
尤其是對iPhone開發來說,更尤為重要。為什麼說更呢?因為iPhone是移動終端,它不會象Mac一樣有很強的CPU,更不會有很多的記憶體,這就使iphone程式在記憶體開銷上變得非常昂貴。所以,如果我們不加限制的話,我們的開發的程式很可能會使用掉所有的記憶體,這樣我們就沒有閒置記憶體來運行其他的程式了,誰都不想在運行程式的時候接不了電話,對吧。所以,在這裡,我們要強掉記憶體管理的重要性。
當然,隨著iPhone的更新換代,它的CPU會越來越強大,它的記憶體會越來越多,即使是這樣我們也要養成良好的編程習慣,隨時考慮記憶體問題,開發出沒有記憶體泄露的完美的程式。
今天,我們將從兩方面討論記憶體這個問題。
1、如何建立對象並向它分配記憶體?
2、如何釋放對象佔用的記憶體?
一、如何分配記憶體
我們使用alloc和init方法來建立對象並初始化對象,這其中就包含了向對象分配記憶體。我們看一下下面的代碼。
- Fraction *myFract = [[ Fraction alloc ] init];
其中Fracion是一個類,上面語句建立了Fraction類的一個新對象myFract,並給它分配了記憶體空間用來儲存在它當中的變數。這是一個很簡單的語句,在你想給任何一個類建立新的對象時,你都可使用這條語句。
還有很多其他的方法可以用來獲得對象,就像前面做的一樣,下面是一些例子。
- NSString *newDisplay = [display.text stringByAppendingString:digit];
- NSArray *keys = [dictionary allKeys];
- NSString *lowerString = [string lowercaseString];
- NSNumber *n = [NSNumber numberWithFloat:42.0];
- NSDate *date = [NSDate date];
上面的這些方法都會獲得對象,還有很多類似的方法,現在,關鍵問題出來了,我們給這些對象分配了記憶體,那誰負責釋放他們呢?我們來看看下面的東西。
二 、記憶體的釋放
好了,我們接著上面留下的問題,為瞭解決這個問題,我要介紹下面幾個概念。
a、引用計數器(Reference Counting)
b、引用所有權(Ownership)
c、自動釋放池(Autoreleasepool)
引用計數器
我們在談論對象記憶體的釋放,可到底什麼時候我們需要釋放對象呢?判斷對象是否需要釋放的唯一標準是:對象不再被使用,它在程式中沒有了利用的價值。我們就是使用引用計數器來判斷對象在程式中是否還在被使用。
引用計數器就是用來跟蹤對象的引用次數,它是這樣工作的:當一個對象被建立時,將它的引用次數設定為1,每當程式中的其他對象或方法調用它時,它的計數器就加1。當調用結束時,計數器就減1,這就意味著這個方法對對象的使用結束了。當一個對象的計數器為0的時候,表示這個對象不被程式中任何其他的對象和方法調用,也就是程式不再需要它,這時候我們就可以安全的釋放它了。
這裡面有兩個非常重要的方法,一個是retain保持),另一個是release釋放)。當對象被引用時,我們向對象發送retain方法,使它的計數器加1.當引用結束後,我們向他發送release方法,使它的計數器減1.這樣看來,retain和release方法是成對出現的,每一個retain對應一個release,同理,我們還會有一個release方法和計數器的初始值1相對應,這樣才能保證計數器可以減到0,確保對象可以被釋放。
所有權Ownership)
你可能對上面的介紹感到有些抽象,難於理解。下面我們介紹所有權的概念,它會有助於你對計數器的理解。在計數器的介紹中我們說一個對象被一個對象所引用,例如對象myFract被對象myNumbe所引用,我們就說myNumbe對Fract有所有權。對應的,當myNumbe不再使用Fract時,它就要放棄對Fract的所有權。當程式中沒有對象和方法對Fract有所有權時,對象Fract就可以被釋放了。這裡還要強調一點,一個對象可以被多個對象或方法所擁有,也就是說程式中可以有多個對象或方法同時對同一個對象擁有所有權。
好了,接下來我們把計數器和所有權聯絡在一起。
當對象A被對象B(或方法)調用時,對象B向A發送retain訊息來獲得對A的所有權,於此同時A的計數器加1;
當B對A的調用結束後,對象B向A發送release訊息來放棄對A的所有權,與此同時A的計時器減1;
當A的計數器為0時,也就是程式中不再有人對A有所有權,這時,A就可以被安全的釋放。
現在明白了嗎?
自動釋放池
我們來說最後一個概念,我們要把這個問題和前兩個概念聯絡在一起,因為它們都是Cocoa記憶體管理的組成部分。我們來談自動釋放池。
系統使用自動釋放池來跟蹤對象,以便以後釋放它們。有些對象在你使用完之後,你並不確定你是否還會再次使用到它,如果這時候就釋放了,以後在用到它的時候就會很麻煩。自動釋放池可以解決這個問題。因為你可以通過標記把對象添加到自動釋放池中,在自動釋放池被釋放的時候,它所包含的對象也會被一起釋放。當對象被添加到釋放池中後,它並沒有被釋放,所以你還可以使用它,但你也不用擔心自己忘記釋放它,應為你已經把它添加到釋放池中了。
下面這條語句用來建立釋放池:
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
這條語句用來釋放自動釋放池:
- [pool drain];
要把對象添加到自動釋放池中,可以使用下面的語句:
- [ myFract autorelease] ;
有一點要注意,把對象添加到釋放池並不會使對象的計數器加1.
三、記憶體管理規則摘要
a釋放對象,可以釋放它所佔有的記憶體,如果你的程式在運行時建立了很多個物件,應該關注對象的釋放,良好的規則是,不再使用建立或保持的對象時,就釋放它們。
b發送一條release訊息不一定銷毀對象,當一個對象的引用計數器為0時,才銷毀這個對象。系統通過向該對象發送一條dealloc訊息來釋放它所佔的記憶體。
c自動釋放池用於在釋放本身時自動釋放池中的對象。
d如果你的方法中不再需要一個對象,但需要返回它時,那麼向其發送一條autorelease訊息,將它標記為以後釋放。
e如果使用alloc和copy方法直接建立對象,則由你負責釋放它。每次retain對象,應該release或autorelease它。
小結:瞭解Cocoa中記憶體如何管理的內容介紹完了,以上這些是Cocoa記憶體管理的基本概念,也是最為重要的概念。對記憶體管理的關注要貫穿程式開發的始終,我們要一再的強調它的重要性。最後希望本文對你有所協助!