顯示具有 iPad 標籤的文章。 顯示所有文章
顯示具有 iPad 標籤的文章。 顯示所有文章

2011年2月15日 星期二

iPhone/Mac Objective-C 記憶體管理教程和原理剖析


版權聲明
此文版權歸作者Vince Yuan (vince.yuan#gmail.com)所有。歡迎非營利性轉載,轉載時必須包含原始鏈接http://vinceyuan.cnblogs.com/,且必須包含此版權聲明的完整內容。
版本 1.1  發表於2010-03-08
前言
初學objectice-C的朋友都有一個困惑,總覺得對objective-C的內存管理機制琢磨不透,程序經常內存洩漏或莫名其妙的崩潰。我在這裡總結了自己對objective-C內存管理機制的研究成果和經驗,寫了這麼一個由淺入深的教程。希望對大家有所幫助,也歡迎大家一起探討。

此文涉及的內存管理是針對於繼承於NSObjectClass
 基本原理
Objective-C的內存管理機制與.Net/Java那種全自動的垃圾回收機制是不同的,它本質上還是C語言中的手動管理方式,只不過稍微加了一些自動方法。
1           Objective-C的對象生成於堆之上,生成之後,需要一個指針來指向它。
ClassA *obj1 = [[ClassA alloc] init];

2           Objective-C的對象在使用完成之後不會自動銷毀,需要執行dealloc來釋放空間(銷毀),否則內存洩露。
[obj1 dealloc];
         這帶來了一個問題。下面代碼中obj2是否需要調用dealloc
ClassA *obj1 = [[ClassA alloc] init];
ClassA *obj2 = obj1;
[obj1 hello]; //輸出hello
[obj1 dealloc];
[obj2 hello]; //能夠執行這一行和下一行嗎?
[obj2 dealloc];
         不能,因為obj1obj2只是指針,它們指向同一個對象,[obj1 dealloc]已經銷毀這個對象了,不能再調用[obj2 hello][obj2 dealloc]obj2實際上是個無效指針。
         如何避免無效指針?請看下一條。

3           Objective-C采用了引用計數(ref count或者retain count)。對象的內部保存一個數字,表示被引用的次數。例如,某個對象被兩個指針所指向(引用)那麼它的retain count2。需要銷毀對象的時候,不直接調用dealloc,而是調用releaserelease會讓retain count1,只有retain count等於0,系統才會調用dealloc真正銷毀這個對象。
ClassA *obj1 = [[ClassA alloc] init]; //對象生成時,retain count = 1
[obj1 release]; //release使retain count1retain count = 0dealloc自動被調用,對象被銷毀
我們回頭看看剛剛那個無效指針的問題,把dealloc改成release解決了嗎?
ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1
ClassA *obj2 = obj1; //retain count = 1
[obj1 hello]; //輸出hello
[obj1 release]; //retain count = 0,對象被銷毀
[obj2 hello];
[obj2 release];
         [obj1 release]之後,obj2依然是個無效指針。問題依然沒有解決。解決方法見下一條。

4           Objective-C指針賦值時,retain count不會自動增加,需要手動retain
ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1
ClassA *obj2 = obj1; //retain count = 1
[obj2 retain]; //retain count = 2
[obj1 hello]; //輸出hello
[obj1 release]; //retain count = 2 – 1 = 1
[obj2 hello]; //輸出hello
[obj2 release]; //retain count = 0,對象被銷毀
問題解決!注意,如果沒有調用[obj2 release],這個對象的retain count始終為1,不會被銷毀,內存洩露。(1-4可以參考附件中的示例程序memman-no-pool.m)
這樣的確不會內存洩露,但似乎有點麻煩,有沒有簡單點的方法?見下一條。

5           Objective-C中引入了autorelease pool(自動釋放對象池),在遵守一些規則的情況下,可以自動釋放對象。(autorelease pool依然不是.Net/Java那種全自動的垃圾回收機制)
5.1          新生成的對象,只要調用autorelease就行了,無需再調用release
ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1 但無需調用release

5.2          對於存在指針賦值的情況,代碼與前面類似。
ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1
ClassA *obj2 = obj1; //retain count = 1
[obj2 retain]; //retain count = 2
[obj1 hello]; //輸出hello
//對於obj1,無需調用(實際上不能調用)release
[obj2 hello]; //輸出hello
[obj2 release]; //retain count = 2-1 = 1

細心的讀者肯定能發現這個對象沒有被銷毀,何時銷毀呢?誰去銷毀它?(可以參考附件中的示例程序memman-with-pool.m)請看下一條。

6           autorelease pool原理剖析。(其實很簡單的,一定要堅持看下去,否則還是不能理解Objective-C的內存管理機制。)
6.1          autorelease pool不是天生的,需要手動創立。只不過在新建一個iphone項目時,xcode會自動幫你寫好。autorelease pool的真名是NSAutoreleasePool
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6.2          NSAutoreleasePool內部包含一個數組(NSMutableArray),用來保存聲明為autorelease的所有對象。如果一個對象聲明為autorelease,系統所做的工作就是把這個對象加入到這個數組中去。
ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1,把此對象加入autorelease pool
6.3          NSAutoreleasePool自身在銷毀的時候,會遍歷一遍這個數組,release數組中的每個成員。如果此時數組中成員的retain count1,那麼release之後,retain count0,對象正式被銷毀。如果此時數組中成員的retain count大於1,那麼release之後,retain count大於0,此對象依然沒有被銷毀,內存洩露。
6.4          默認只有一個autorelease pool,通常類似於下面這個例子。
int main (int argc, const char *argv[])
{
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];

// do something

[pool release];
return (0);
} // main
所有標記為autorelease的對象都只有在這個pool銷毀時才被銷毀。如果你有大量的對象標記為autorelease,這顯然不能很好的利用內存,在iphone這種內存受限的程序中是很容易造成內存不足的。例如:
int main (int argc, const char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int i, j;
for (i = 0; i < 100; i++ )
{
 for (j = 0; j < 100000; j++ )
    [NSString stringWithFormat:@"1234567890"];//產生的對象是autorelease的。
}
[pool release];
return (0);
} // main
(可以參考附件中的示例程序memman-many-objs-one-pool.m,運行時通過監控工具可以發現使用的內存在急劇增加,直到pool銷毀時才被釋放)你需要考慮下一條。

7           Objective-C程序中可以嵌套創建多個autorelease pool。在需要大量創建局部變量的時候,可以創建內嵌的autorelease pool來及時釋放內存。(感謝網友hhyyttneogui的提醒,某些情況下,系統會自動創建autorelease pool, 請參見第四章)
int main (int argc, const char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int i, j;
for (i = 0; i < 100; i++ )
{
 NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
 for (j = 0; j < 100000; j++ )
    [NSString stringWithFormat:@"1234567890"];//產生的對象是autorelease的。
 [loopPool release];
}
[pool release];
return (0);
} // main
(可以參考附件中的示例程序memman-many-objs-many-pools.m,佔用內存的變化極小)

iPhone,iPad Icon格式說明

http://developer.apple.com/library/ios/#qa/qa2010/qa1686.html


iPhone-only Apps

Include the following in your application's Resources group in the Xcode project:
Table 1 : iPhone-only apps icon requirements.
Image Size (px)File NameUsed ForRequired StatusNotes
512x512iTunesArtworkAd Hoc iTunesOptional but recommendedFile should be in png format, but name it without the .png extension
57x57Icon.pngApp Store and Home screen on iPhone/iPod touchRequired<None>
114x114Icon@2x.pngHome screen for iPhone 4 High ResolutionOptional but recommended<None>
72x72Icon-72.pngHome screen for iPad compatibilityOptional but recommended<None>
29x29Icon-Small.pngSpotlight and SettingsOptional but recommended<None>
50x50Icon-Small-50.pngSpotlight for iPad compatibilityRecommended if you have a Settings bundle, otherwise optional but recommended<None>
58x58Icon-Small@2x.pngSpotlight and Settings for iPhone 4 High ResolutionRecommended if you have a Settings bundle, otherwise optional but recommended<None>

iPad-only Apps

Include the following in your application's Resources group in the Xcode project:
Table 2 : iPad-only apps icon requirements.
Image Size (px)File NameUsed ForRequired StatusNotes
512x512iTunesArtworkAd Hoc iTunesOptional but recommendedFile should be in png format, but name it without the .png extension
72x72Icon-72.pngApp Store and Home screen on iPadRequired<None>
50x50Icon-Small-50.pngSpotlight on iPadOptional but recommended<None>
29x29Icon-Small.pngSettings on iPadRecommended if you have a Settings bundle, otherwise optional but recommended<None>

Universal Apps

Include the following in your application's Resources group in the Xcode project:
Table 3 : Universal apps icon requirements.
Image Size (px)File NameUsed ForRequired StatusNotes
512x512iTunesArtworkAd Hoc iTunesOptional but recommendedFile should be in png format, but name it without the .png extension
57x57Icon.pngApp Store and the Home screen on iPhone/iPod touchRequired<None>
114x114Icon@2x.pngHome screen for iPhone 4 High ResolutionOptional but recommended<None>
72x72Icon-72.pngApp Store and Home screen on iPadRequired<None>
29x29Icon-Small.pngSettings on iPad and iPhone, and Spotlight on iPhoneRecommended if you have a Settings bundle, otherwise optional but recommended<None>
50x50Icon-Small-50.pngSpotlight on iPadOptional but recommended<None>
58x58Icon-Small@2x.pngSpotlight and Settings for iPhone 4 High ResolutionRecommended if you have a Settings bundle, otherwise optional but recommended<None>

Add Icon files in Info.plist

To add the Icon files entry you will need to manually edit your Info.plist.
Open your Info.plist in Xcode, and locate the "Icon file" entry. Click on the plus (+) button at the end of the entry to make a new key entry. as shown in Figure 2.
Figure 2: Add a new entry under "Icon file" key
Figure 2, Add a new entry under "Icon file" key
Then, type "Icon files" in the key column (notice the "s" added), or choose "Icon files" from the drop down list, as shown in Figure 3.
Figure 3: Add "Icon files" key in Info.plist
Figure 3, Add "Icon files" key in Info.plist
Next, turn down the disclosure triangle on "Icon files" and add the entries for the icons for your application, as shown in Figure 4 and Figure 5:
Figure 4: Add icon file entries for iPhone-only or Universal apps.
Figure 4, Add icon file entries for iPhone-only or Universal apps.
Figure 5: Add entries for iPad icon files.
Figure 5, Add entries for iPad icon files.