autorelease
autorelease を使ってオブジェクトを生成した場合、そのオブジェクトはランループ(イベント処理の周期)終了時に解放される。
(例) NSMutableArray* array = [NSMutableArray array];
通常はこの仕組で問題ないが、バッチ的な処理を1箇所で行なう場合に autoreleaeを使うと解放されない大量の autoreleae属性のオブジェクトが残ってしまう場合がある。
(例) for (i=0; i < 100; i++) { NSMutableArray* array = [NSMutableArray array]; : 時間のかかる処理 : }これは、処理が終わるまでランループが終了しないので autoreleae属性のオブジェクトを解放するタイミングがこない為に起こる。
これを回避するには autorelease を使うのをやめて自前で releaseする。
(例) for (i=0; i < 100; i++) { NSMutableArray* array = [[NSMutableArray alloc] init]; : 時間のかかる処理 : [array release]; }
もしくは NSAutoreleasePoolを局所的に作る。
(例) for (i=0; i < 100; i++) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; NSMutableArray* array = [NSMutableArray array]; : 時間のかかる処理 : [pool release]; }こうするとランループでの解放を待たずに autorelese属性のオブジェクトを解放することができる。NSAutoreleasePool はインスタンスが作成されると、それ以降の autorelease属性のオブジェクトを覚えていて、自分が releaseされるタイミングでこれらのオブジェクトへ releaseを投げて解放する。
なお NSAutorelesePoolで管理される autorelease属性のオブジェクトの一覧はプライベートメソッドを使うと見ることができる。
NSLog(@"%@", [NSAutoreleasePool showPools]);こんな感じ。
- -- ---- -------- Autorelease Pools -------- ---- -- - ==== top of stack ================ 0x620d860 (__NSArrayM) 0x6014b20 (NSCFNumber) 0x6019ae0 (__NSArrayI) 0x6019a70 (__NSArrayM) 0x6019b90 (__NSArrayI) 0x6002f00 (__NSArrayM) 0x6018030 (__NSArrayI) 0x60109b0 (__NSCFDictionary) : : ==== top of pool, 102 objects ================ 0x60172d0 (__NSCFData) 0x6013210 (__NSCFData) 0x60130d0 (NSPathStore2) 0x60130b0 (NSCFString) 0x6012fd0 (NSCFString) ==== top of pool, 5 objects ================ ==== top of pool, 0 objects ================
[参考情報] [NSAutoReleasePool][CFRunLoop] NSAutoReleasePoolの管理者は誰であるべきか - Ni chicha, ni limona - 平均から抜けられない僕 - iPhoneアプリ開発グループ
サンプル
効果を確認する為サンプルを作ってみた。1MBの NSDataを連続で100回作成した時のメモリ利用量を NSLog()でデバッグコンソールへ書き出す。
こんな感じ。
#define BUFF_SIZE (1024*1024) static char buff[BUFF_SIZE]; - (void)test { for (int i=0; i < 100; i++) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; [NSData dataWithBytes:buff length:BUFF_SIZE]; メモリ利用量表示処理 [pool release]; } NSLog(@"end"); }
(1)NSAutoreleaePool なしの場合、(2)ありの場合、(3)ありで drainを使った場合の3つを調べた。
(1) NSAutoreleasePoolなしの場合
AutoreleasePoolTest[54677:207] RSS: 13.5 MB AutoreleasePoolTest[54677:207] RSS: 14.8 MB AutoreleasePoolTest[54677:207] RSS: 15.8 MB AutoreleasePoolTest[54677:207] RSS: 16.8 MB AutoreleasePoolTest[54677:207] RSS: 17.8 MB : : AutoreleasePoolTest[54677:207] RSS: 110.9 MB AutoreleasePoolTest[54677:207] RSS: 111.9 MB AutoreleasePoolTest[54677:207] RSS: 112.9 MB AutoreleasePoolTest[54677:207] endforループを1回るたびに 1MB増加しているのがわかる。
(2) NSAutoreleasePoolありの場合(release解放)
AutoreleasePoolTest[54700:207] RSS: 13.5 MB AutoreleasePoolTest[54700:207] RSS: 13.8 MB AutoreleasePoolTest[54700:207] RSS: 13.8 MB : : AutoreleasePoolTest[54700:207] RSS: 13.8 MB AutoreleasePoolTest[54700:207] RSS: 13.8 MB AutoreleasePoolTest[54700:207] RSS: 13.8 MB AutoreleasePoolTest[54700:207] endforループを繰り返しても一定のメモリ利用量から変化しない。ループ内で確保された NSDataはループの終わりできちんと解放されている。
(3) NSAutoreleasePoolありの場合(drain解放)
AutoreleasePoolTest[54700:207] RSS: 13.5 MB AutoreleasePoolTest[54700:207] RSS: 13.8 MB AutoreleasePoolTest[54700:207] RSS: 13.8 MB : : AutoreleasePoolTest[54700:207] RSS: 13.8 MB AutoreleasePoolTest[54700:207] RSS: 13.8 MB AutoreleasePoolTest[54700:207] RSS: 13.8 MB AutoreleasePoolTest[54700:207] endrelease使用時と変わらず。
と、NSAutoreleasePool の効果がよくわかった。
メモリ利用量の取得
現在使用しているメモリ量は task_info() を使うと取得できる。
[参考情報] 自分のアプリが使用しているメモリサイズを取得するには - The iPhone Development Playground
今回のコードはこんな感じ。
struct task_basic_info t_info; mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; if (task_info(current_task(), TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count)!= KERN_SUCCESS) { NSLog(@"%s(): Error in task_info(): %s", __FUNCTION__, strerror(errno)); } u_int rss = t_info.resident_size; NSLog(@"RSS: %0.1f MB", rss/1024.0/1024.0);
ソースコード
GitHubからどうぞ。
AutoreleasePoolTest at 2010-10-04 from xcatsan's iOS-Sample-Code - GitHub
参考情報
[NSAutoReleasePool][CFRunLoop] NSAutoReleasePoolの管理者は誰であるべきか - Ni chicha, ni limona - 平均から抜けられない僕 - iPhoneアプリ開発グループ
自分のアプリが使用しているメモリサイズを取得するには - The iPhone Development Playground
NSAutoreleasePool Class Reference
Memory Management Programming Guide: Autorelease Pools
- - - -
release と drain で挙動が違う現象に遭遇した。その為、今回サンプルを作って確認してみたのだがやはり差異はなかった。うーむ。
Responses
Leave a Response