Zombies インストゥルメントによると、一部のディクショナリ値の過剰リリースが原因で、時折発生するクラッシュに混乱しています。Instruments でこれらのオーバーリリースされたオブジェクトの 1 つのオブジェクト履歴を見ると、その保持カウントが 1 つの段階で +2 から 0 に直接低下していることがわかります。(投稿の最後にあるスクリーンショットを見てください)。これがどのように可能であるかは私には明らかではありません。
このクラッシュは、Instruments でプロファイリングしているときにしか見られないため、Apple のバグであると考えられますが、Instruments が明らかにしているだけのパイロット エラーであると想定する方がおそらく安全です。
とにかく、いくつかの Core Foundation オブジェクト (CFStrings および CFNumbers) を含む CFDictionary を構築しています。次に、これを NSDictionary* にキャストして、Objective-C メソッドに渡します。私のコードの簡略版は以下のとおりです。
// creates a CFDictionary containing some CFStrings and CFNumbers
void doStuff()
{
CFDictionaryRef myDict = CreateMyDictionaryContainingCFTypes();
dispatch_async(myQueue, ^{
[someObject receiveDictionary:(NSDictionary*)myDict];
CFRelease(myDict); // this line causes a crash. The Zombies instrument
// claims a CFString value contained in this
// dictionary has already been freed.
});
}
// ...
- (void)receiveDictionary:(NSDictionary*)dict
{
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSString* str1 = [dict objectForKey:@"key1"];
NSString* str2 = [dict objectForKey:@"key2"];
NSNumber* num1 = [dict objectForKey:@"key3"];
dispatch_async(myOtherQueue, ^{
[database executeUpdate:@"INSERT INTO blah (x,y,z) VALUES (?, ?, ?)", str1, str2, num1];
});
[pool drain];
}
と はObjective-C オブジェクトと見なされるため、呼び出しによってブロックがコピーされるとキャプチャされて自動的に保持str1
され、そのブロックが解放されると解放されると考えていました。実際、これらの変数はブロックによってキャプチャおよび保持されているようです。しかし、Instruments でオーバーリリースされた CFString のオブジェクト履歴を調べると、ブロックがコピーされると参照カウントが増加していることがわかります。困ったことに、ブロックが解放されると保持カウントが +2 から直接 0 に低下します (記事の最後にあるスクリーンショットを参照)。スタック トレースから、これがどのブロックかを判断する方法がわかりません。ブロック内の辞書で呼び出されるまでにstr2
num1
-receiveDictionary:
dispatch_async
CFRelease
doStuff()
、その値の一部は既に割り当てが解除されており、プログラムがクラッシュします。
では、追加のリリース コールはどこから来たのでしょうか。Instruments が示すように、オブジェクトの保持カウントが +2 から 0 に直接低下するのはなぜでしょうか?
気まぐれで、次のように、2 番目のブロックに辞書全体を強制的に保持させました。
dispatch_async(myOtherQueue, ^{
[database executeUpdate:@"INSERT INTO blah (x,y,z) VALUES (?, ?, ?)", str1, str2, num1];
[dict self];
});
これにより、クラッシュが消えたようです。少なくとも、インストゥルメントはゾンビの報告を停止します。ただし、これが機能する理由は一生わかりません。確かに、ブロックが辞書全体ではなく、関心のある辞書の値を保持していることを確認するだけです。どうしたの?
Instruments は、オブジェクトの保持カウントとともに、ゾンビ CFString の次のオブジェクト履歴を一覧表示します。興味深いイベントのスクリーンショットを含めました。
#0 +1 CFString を作成
#1 +2 CFString をディクショナリに追加
#2 +1 CFString を解放
#3 +2のブロックを-receiveDictionary:
コピー
しても CFString を保持
#4 +0なに…?オブジェクトのリテイン カウントが +2 から 0 に急降下しました!
#5 -1 CFDictionary が解放され、クラッシュする