Objective-Cブロックは、バックグラウンドのみのアプリケーション用にリリースされていません
質問
バックグラウンドでのみ実行されるアプリケーションがあります(指定することによって LSBackgroundOnly
info.plistファイル)。問題は、並列キューで実行するすべてのブロックがリリースされていないことです。コードはメモリ管理された環境で実行されます - GCは関与しません。
(単純化された)コードは以下のようになります。 Blubberは、テスト用のNSDateを保持するダミークラスにすぎません。また、それは上書きされます retain
, release
, 、 と dealloc
ロギングを行うには:
NSOperationQueue *concurrentQueue = [[NSOperationQueue alloc] init];
[concurrentQueue setMaxConcurrentOperationCount:NSOperationQueueDefaultMaxConcurrentOperationCount];
Blubber *aBlubber = [[Blubber alloc] init];
aBlubber.aDate = [NSDate date];
[concurrentQueue addOperationWithBlock:^{
NSAutoreleasePool *blockPool = [[NSAutoreleasePool alloc] init];
NSDate *test = [aBlubber aDate];
NSLog(@"Block DONE");
[blockPool release];
}];
[aBlubber release];
[concurrentQueue release];
アプリケーションを通常の(つまり、非バックガウンド)アプリケーションに変更すると、UIを介して入力が行われるたびにリリースされるブロックを観察できます(焦点を別のウィンドウに変更するだけでも十分です)。私のBackgorundアプリはHID USBドライバー上に直接入力を受け取るので、ウィンドウやメニューバーがないため、これは起こりません。
Runloopを手動で強制する方法や、完成したブロックをリリースするようにキューに伝える責任があるものはありますか?
(ブロックによって保持されていた他のすべてのオブジェクトもリリースされておらず、大きなメモリリークを作成します。これらのリークは、漏れやオブジェクトアロケーションツールによってスポットすることはできませんが、メモリ消費はTOPを使用して急上昇するのを観察できます。)
解決
Autoreleaseプールの一般的な「Gotcha」の1つは、アプリがイベントを受信せずにメモリを構築している場合、 最も外側 プール(イベントループで管理されているもの)は排出されません。
あなたがあなた自身のプールを管理しているので、私はそれがここに適用されるべきではないと思います...しかし、念のため、あなたはこれを試すことができます:
...
//When no events are coming in (i.e. the user is away from their computer), the runloop doesn't iterate, and we accumulate autoreleased objects
[[NSTimer scheduledTimerWithTimeInterval:60.0f target:self selector:@selector(kickRunLoop:) userInfo:nil repeats:YES] retain];
...
- (void) kickRunLoop:(NSTimer *)dummy
{
// Send a fake event to wake the loop up.
[NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
location:NSMakePoint(0,0)
modifierFlags:0
timestamp:0
windowNumber:0
context:NULL
subtype:0
data1:0
data2:0]
atStart:NO];
}
他のヒント
ブロックが範囲外になった後に使用されるスタックベースのブロックを使用しているようです。ブロックをコピーする必要があります。これに変更された場合、コードは機能するはずです。
[concurrentQueue addOperationWithBlock:[[^{
NSAutoreleasePool *blockPool = [[NSAutoreleasePool alloc] init];
NSDate *test = [aBlubber aDate];
NSLog(@"Block DONE");
[blockPool release];
}copy]autorelease]];
ブロックに関する完全な記事については、この投稿をご覧ください。 http://gkoreman.com/blog/2011/02/27/blocks-in-clujective-c/