2011年2月7日 星期一

Objective-C 字串append與記憶體管理的奧義

首先要說明一下NSString與 NSMutableString
在Objective-C中 NSString是一種不可變更的字串物件,而且是以unicode編碼的字串與一般C/C++中的 Char* 字元陣列所組成的字串不同. 最大的不同點在於每一個字所使用的byte數 ; 而NSMutableString就是一種可變更的字串物件. 說到這一定一堆人會提出很多的程式語法說明NSStrig是可變更的物件,如:
NSString *str = @"string 1";
str = [str StringByAppendingString:@":string 2"];
//此時str內容即為 @"string 1:string2"

是的以上語法沒有錯, str內容也確實為二個字串的合併. 對於Objective-C的記憶體管理還沒有深入瞭解的人們地確會認為“NSString怎麼會是不可變更的字串物件呢?” ,這個問題對於筆者來說也一樣的有過問號,但是查遍了相關的書籍,問過無數次google大神.始終沒有得到一個很好的答案.

就在我不想理會這個問題,而在設計”iPhone 六法全書“之時出現一個相當怪的麻煩:民法有1225條款而使用StringByAppendingString 太多次會(約100次)在Device 中當掉,但在模擬器中確不會有這個問題.
這下可好,這種模擬器與實機測試的結果不同相當難處理,問過google大神後,得知也有人遇到此問題但是沒人解答出來,我只好摸著頭進行Device Debug . 結果是在相當程度的loop使用StringByAppendingString後,原本的字串會突然變為“nil”. 接下來就完全不知如何解決哩.

此時我在想會不會是iPhone SDK目前的bug,於是我換一個function試試看,我改用"stringByAppendingFormat",這個function比“ StringByAppendingString”好使用多了,程式碼 也減少許多.但仍然在Device 會當掉.

於是我開始想著有沒有可能是記憶體管理的問題呢?在一本“Programming in Objective-C 2.0”, 2nd Edition 的某章節中有一小段寫著大致內容為”StringByAppendingString 會建立一個由二字串字合併組成的新字串物件“(非原文引用),所以說以剛才的程式為例
str = [str StringByAppendingString:@":string 2"];
//此時str為一個新的字串物件,那原本的字串物件呢?
原兇出現了, 就是“原本的字串物件呢?”, 此時有人會說Objective-C 有autorelease的機制. 沒錯是有autorelease機制,但此時並沒有被relase掉喔.(本篇並不討論release問題,請各自詳讀Objective-C的記憶體管理)因此有太多的字串物件沒有被立即release而導致程式crash.

解說到此希望有人看得慬我所要說明的,而且能瞭解NSString是不可變更的字串物件.

所以我的問題就得請出原本一直以為是多餘的一個字串物件NSMutableString,即所謂的可變更的字串物件.而修改後的程式大致如下:
NSMutableString *str = [[NSMutableString alloc] init];
while(...) {
[str appendFormat:@"..."]; // 不需回傳,因為是原串字直接append
}

如此一來就不會一直create一堆沒用的NSString在記憶體內啦.

此事讓我對除了Pascal 以外第二個欣賞的語言就是Objective-C