FW: http://blog.csdn.net/dboylx/archive/2009/02/15/3893770.aspx
讀了上篇記憶體管理文檔後,已經對Objective-c記憶體管理機制有了初步的瞭解。但僅賃那個其實還遠遠不夠,真正的項目中仍然會遇到記憶體管理上的問題。在具備了基礎的理論知識後,還需要些記憶體管理上的技巧與經驗。這點由其對從JAVA類語言過來的程式員,咱們實話實說,記憶體管理真不是咋們長項,更需要花更多的時間與精力來積累相關知識。不過話又說回來,人都說做生意的都講究吃虧在前賺錢在後。開始時候多賣點力,這座美麗的“金山”早晚是我們的。 ^_^
1,把對象視為記憶體。
每個對象執行個體的狀態都被儲存在不同的記憶體地區中。因此對象的建立與刪除動作等價於它所佔用記憶體的分配與回收。基於Foundation的類庫,都通過一個根對象“NSObject”或有同樣介面的其它類,為執行個體提供關聯記數的機制(包括延遲釋放的對象)。大多在Apple類庫的類與基於“NSObject”的子類或它介面的實作類別,都可以享有記憶體記數策略帶來的管理能力。
Apple類庫(Apple's frameworks)出台後,記憶體管理機制就被放在對象建立與銷毀的生命週期中(雖然有些地方還是些C的方法與結構)。你會發現這其實是件很有意思的事情,它不像C一樣有方法直接操作記憶體(malloc/free),也不像帶GC的語言(Java & Smalltalk & Ruby & Python ...)自動管理記憶體。 它是一種基於關聯記數與延時釋放(Autorelease機制,下面會講到)的機制管理記憶體,我們可以認為它是基於以上兩種記憶體管理方案中間,一個比較中庸的記憶體管理解決方案。
2,對象持有制
(Object Ownership,不知道要翻譯成啥,暫時叫他持有制,其實這個叫法本身就有些字面上的誤導。瞭解它的本質就OK了,不必太在意它叫什麼。^_^)
基礎類庫與其它類庫都推薦我們下面兩個對象建立與銷毀的策略:
a 如果你建立了一個對象,你有責任把它銷毀
b 如果你想持有一個並不是你建立的對象,你需要”retain”它,並在不需要時”release”掉
首先,對象的建立者就是它的擁有者,只有它的擁有者才可以銷毀它。貫徹這條策略會使你的代碼變的更簡單,更強壯,並且可以繞開很多的引用已經銷毀對象或記憶體泄露(沒有用的對象卻始終保持關係)造成的BUG。使用”NSAutoreleasePool”可以實現延遲釋放機制,建立者可以把銷毀的責任交給”NSAutoreleasePool”的對象執行個體(在下面會有詳細原理說明)。
3,對象的記憶體配置與初始化
SomeClass *anInstance = [[SomeClass alloc] init];
這是一個傳統的建立對象的方法,首先分配一段記憶體,然後初始化。另外,在作業系統層面上還有記憶體區的概念,為了提高記憶體地區的定位使用能力,可以用”allocWithZone:”方法來嘗試分配一段指定的地區。”NSObject”的有狀態的子類都必需要擴充初始化方法,例如:
@interface CartesianCoordinate : NSObject
{
NSNumber *abscissa;
NSNumber *ordinate;
}
- (CartesianCoordinate *) initWithAbscissa: (NSNumber *)anAbscissa
ordinate: (NSNumber *)anOrdinate;
@end
“NSObject”還提供了”copy,mutableCopy,copyWithZone,mutableCopyWithZone”方法可以分配記憶體,複屬性 來達到複製對象執行個體的目的。
4,對象的回收
如果你不想再使用一個對象時,就發送”release”的訊息。當所有人都不在使用它,當沒有任何一個關聯時,它就會被自動發送”dealloc”方法回收。持有屬性的類,應該它在的”dealloc”方法內釋放所有它持有的對象執行個體。
@implementation CartesianCoordinate
...
- (void) dealloc
{
[abscissa release];
[ordinate release];
return [super dealloc];
}
@end
5,對象執行個體的關聯記數
其實你應該可以瞭解到,關聯記數是一個非常非常簡單的事情。每個對象都持有一關聯記數器”retain count”,它僅僅負責記錄關聯它的總個數。當一個對象以”init,initWith...”或其它複製方法建立時,這個數就被系統隱式的記為”1”。所有其它的對象可以發送”retain”訊息持有它,這個方法也僅僅是在這個記數上加”1”而已。相對應的,每一個”realease”方法也只是把這個數減”1”。當它為”0”時,這個對象被回收(調用它的”dealloc”方法)。你也可以調用”retainCount”方法來查詢這個數字。
- (void) notifyUserOfError: (NSString *)errorString
{
NSMutableString *alertString = nil;
alertString = [[NSMutableString alloc] initWithString:
@"The following error occurred: "];
[alertString appendString: errorString];
NSRunAlertPanel( alertString ...);
[alertString release];
return;
}
6,臨時對象與自動釋放方法
就像你上面看到的,經常需要建立一個只用一次的對象,然後銷毀它。在上面的例子裡,當範圍定義好後它是很簡單的一件事。但存在一個問題,不能返回一個臨時的對象給調用者!!! 在C語言中有一個常用的方法,就是使用已經存在的靜態緩衝或是返回動態分配的記憶體,可能你已經想到了,它的調用者負責釋放它。這個方案跟咱們上面提到的記憶體管理原則相左,在Foundation架構中已經提供了一個更優雅的解決方案。通過延遲釋放機制讓建立臨時的對象可以最終自動釋放,看以下代碼:
- (void) notifyUserOfError: (NSString *)errorString
{
NSMutableString *alertString = nil;
alertString = [NSMutableString stringWithString:
@"The following error occurred: "];
[alertString appendString: errorString];
NSRunAlertPanel( alertString ...);
return;
}
你可以看到”alertString”對象在建立後並沒有調用”release”,而這個方法的調用者也不用擔心要不要去釋放它,因為在這裡建立的對象是一個”autoreleased”對象,而這種對象會被自動釋放。一個自動釋放對象會在將來的某一時間被自動調用”release”方法。自動釋放對象被建立後,若沒有被顯示的”retain”,在有限的生命週期中被會自動銷毀。如果你想指定一個對象為自動釋放的話,可以調用”autorelease”方法。
alertString = [NSMutableString stringWithString:
@"The following error occurred: "];
完全和以下是一樣的:
alertString =
[[[NSMutableString alloc] initWithString:
@"The following error occurred: "] autorelease];
有這麼一個貫例,就是像stringWithString:類似的方法都會建立一個自動釋放的執行個體,在類庫裡隨處可見。
7,自動釋放進階,讓我們更深一步瞭解它的工作原理
雖然自動釋放對象的概念是如此簡單,但瞭解它更多的工作原理還是很有必要的。不然在我們的嵌入式裝置的開發中,仍然會走入記憶體漏洞深淵。
其實,在我們的一個應用中,是有很多的”NSAutoreleasePool”對象執行個體的,就像它的命名一樣,它們用來收集所有自動釋放的對象。只要在調用”autorelease”方法後,它就會被加入到這個池中。在未來的某個時刻,一般指在”Foundation”與”AppKit”應用一個事件迴圈結束時,或者在響應完”WebObjects”類應用請求時,或調用”NSAutoreleasePool”對象的”release”方法。這裡需要注意,”NSAutoreleasePool”並不止一個,為什麼需要多個”NSAutoreleasePool”來管理記憶體呢?因為,在一個程式碼片段內就回收所有自動釋放對象是很有用處的,多線程應用中,每個線程可以擁有一個自動釋放池的棧,當你建立了一堆臨時對象時,而僅僅是在一段很短的上下文中,比如一個簡單迴圈,你並不希望在下面的代碼中,他們仍然佔用保寶貴的記憶體資源,你就可以為這段短小緊湊本機內容建立一個”NSAutoreleasePool”對象來管理他們:
- (id) findSomething
{
id theObject = nil;
// Whatever we're looking for
NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init];
// Autoreleased objects are now automatically placed in localPool.
// Loop that creates many temporary objects
while ( theObject == nil )
{
...
if ( [temporaryObject matchesSomeCondition] )
{
theObject = [temporaryObject retain];
// We want this one
}
}
// Get rid of all those temporary objects
[localPool release];
return [theObject autorelease];
}
上段代碼我們做了什麼:
A,我們建立了一個”NSAutoreleasePool”對象,把它壓到當前上下文中的記憶體管理池頂,在它下面的所有自動記憶體管理對象都被放入到這個池中。
B,我們調用了自動釋放對象”temporaryObject”的”retain”方法,使它的生命週期超過本地池的管理。
C,釋放池操作,同時把它從棧中POP出去。
D,緊接著,我們又在返回它前調用了 ”autorelease”方法,把這個對象放入當前池棧的TOP池中。
這東西有點繞,但原理其實還算簡單。如果上面的能明白了,恭喜你,Objective-C的水平又上了一個台階。
同理可證,還有一段更精練的代碼:
- (NSArray *) findAListOfThings
{
NSMutableArray *thingArray =
[[NSMutableArray alloc] initWithCapacity: 25];
// The list of 25 things we're looking for
NSAutoreleasePool *outerPool = [[NSAutoreleasePool alloc] init];
NSAutoreleasePool *innerPool = nil;
NSArray *largeObjectArray = nil;
id temporaryObject = nil;
NSEnumerator *arrayEnumerator = nil;
// Loops that create many temporary objects
while ( [thingArray count] != 25 )
{
largeObjectArray = [self fetchLotsOfObjects];
// largeObjectArray is autoreleased and contained in the
// outer autorelease pool
arrayEnumerator = [largeObjectArray objectEnumerator];
// Note that the enumerator itself is a temporary object!
// It will be released by the outerPool
// Create the inner pool on each iteration. When
// a pool is created, it automatically becomes the
// "top" pool on the current thread's stack of pools.
innerPool = [[NSAutoreleasePool alloc] init];
// autoreleased objects now go into innerPool
while ( temporaryObject = [arrayEnumerator nextObject] )
{
...
if ( [temporaryObject matchesSomeCondition] )
{
[thingArray addObject: temporaryObject];
// Collections retain their members
}
}
// Dispose temporary objects created on this iteration;
// Note that the objects added to thingArray during this
// iteration are also in innerPool and thus sent a release
// message, but are not destroyed because they have been
// retained by thingArray and so have an additional reference
// (their retainCount > 1)
[innerPool release];
}
[outerPool release];
return [thingArray autorelease];
}
寫到這裡所有的概念都已經講完了,記憶體管理的東西也就是這麼多???
有興趣的可以在這裡再討論些執行個體?或是發我郵箱?MSN?
翻譯自:
http://www.stepwise.com/Articles/Technical/MemoryManagement.html