NSAutoreleasePool 再び
あれid:masakih:20110113:1294933491を書いたあと、例外以外でも大丈夫なんだろうかと思ったのでやってみたよ!
- (void)test01 { NSAutoreleasePool *pool01 = [[NSAutoreleasePool alloc] init]; for(int i = 0; i < 100; i++) { NSAutoreleasePool *pool02 = [[NSAutoreleasePool alloc] init]; Hoge *hoge = [[[Hoge alloc] init] autorelease]; if(i>3) break; [pool02 release]; // 途中 break対策されてません! } NSLog(@"Before pool01 release"); [pool01 release]; NSLog(@"After pool01 released"); }
結果
2011-01-20 19:57:07.618 TestAutoreleasePool[30389:a0f] Initialized -> 0x1001c8680 2011-01-20 19:57:07.619 TestAutoreleasePool[30389:a0f] Deallocate -> 0x1001c8680 2011-01-20 19:57:07.619 TestAutoreleasePool[30389:a0f] Initialized -> 0x1001c89a0 2011-01-20 19:57:07.620 TestAutoreleasePool[30389:a0f] Deallocate -> 0x1001c89a0 2011-01-20 19:57:07.621 TestAutoreleasePool[30389:a0f] Initialized -> 0x1001c8d10 2011-01-20 19:57:07.622 TestAutoreleasePool[30389:a0f] Deallocate -> 0x1001c8d10 2011-01-20 19:57:07.623 TestAutoreleasePool[30389:a0f] Initialized -> 0x1001c9040 2011-01-20 19:57:07.623 TestAutoreleasePool[30389:a0f] Deallocate -> 0x1001c9040 2011-01-20 19:57:07.624 TestAutoreleasePool[30389:a0f] Initialized -> 0x1001c9370 2011-01-20 19:57:07.628 TestAutoreleasePool[30389:a0f] Before pool01 release 2011-01-20 19:57:07.628 TestAutoreleasePool[30389:a0f] Deallocate -> 0x1001c9370 2011-01-20 19:57:07.629 TestAutoreleasePool[30389:a0f] After pool01 released
途中Breakがあっても大丈夫です!
さて、ここからが問題。じゃあ、NSAutoreleasePool自体はどうなのさ?
残念ながらまだi368/X86_64のニーモニックとか全然分からないのでobjcランタイムで分かる事だけ。
まず、NSAutoreleasePoolのメソッドを乗っ取ります。
@implementation NSAutoreleasePool (hoge) + (void)load { if(self == [NSAutoreleasePool class]) { Method method01 = class_getClassMethod(self, @selector(allocWithZone:)); Method method02 = class_getClassMethod(self, @selector(allocWithZoneA:)); method_exchangeImplementations(method01, method02); method01 = class_getInstanceMethod(self, @selector(init)); method02 = class_getInstanceMethod(self, @selector(initA)); method_exchangeImplementations(method01, method02); method01 = class_getInstanceMethod(self, @selector(release)); method02 = class_getInstanceMethod(self, @selector(releaseA)); method_exchangeImplementations(method01, method02); method01 = class_getInstanceMethod(self, @selector(drain)); method02 = class_getInstanceMethod(self, @selector(drainA)); method_exchangeImplementations(method01, method02); method01 = class_getInstanceMethod(self, @selector(dealloc)); method02 = class_getInstanceMethod(self, @selector(deallocA)); method_exchangeImplementations(method01, method02); } } + (id)allocWithZoneA:(NSZone *)zone { id res = [self allocWithZoneA:zone]; fprintf(stderr, "Allocated NSAutoreleasePool <%p>\n", res); return res; } - (id)initA { [self initA]; fprintf(stderr, "Initialized NSAutoreleasePool <%p>\n",self); return self; } - (void)releaseA { fprintf(stderr, "Release NSAutoreleasePool <%p>\n", self); [self releaseA]; } - (void)drainA { fprintf(stderr, "Drain NSAutoreleasePool <%p>\n", self); [self drainA]; } - (void)deallocA { fprintf(stderr, "Deallocated NSAutoreleasePool <%p>\n", self); [self deallocA]; } @end
その上で、上のコードを実行しました。さらに、このプロジェクトはCocoaアプリケーションテンプレートを使用したので、勝手にメニューとウインドウがついてます。これをグリグリ動かしました。さらにウインドウにボタンを付けて-test01メソッドを何度か実行してみました。出力が長くなるので以下のコード(awk)で見やすくしました。
NF!=3 { next } /Alloca/{ alloc[$3]++ } /Init/ { init[$3]++ } /Release/ { release[$3]++ } /Drain/ { release[$3]++ } /Dealloc/ { dealloc[$3]++ } END { printf("Pointer----\tAlloc\tInit\tRelease/Drain\tDealloc\n") for(p in alloc) { printf("%s\t%5d\t%4d\t%13d\t%7d\n", p, alloc[p], init[p], release[p], dealloc[p]) } }
その結果。
Pointer---- Alloc Init Release/Drain Dealloc <0x10017bfe0> 5 5 4 0 <0x1006986d0> 15 15 15 0 <0x100180b40> 5 5 4 0 <0x10017e0a0> 5 5 4 0 <0x10068b650> 8 8 7 0 <0x1006991e0> 142 142 141 0 <0x114b6b3c0> 5 5 4 0 <0x100699210> 5 5 4 0
なんと、全然deallocされてません。
あれ?
しかも、同じインスタンスを使い回してる?
うーーん。なぞです。
ただし、アクティビティモニタで見る限りリークが発生しているので、releaseしていないNSAutoreleasePool自体はリークしているようです。
てことで、とりあえずは、確保したNSAutoreleasePoolはちゃんと解放しましょう!