Dispatch_semaphore_disposeでEXC_BAD_INSTRUCTION(code = EXC_I386_INVOP、subcode = 0x0)を取得していますが、これの根本原因を追跡する方法を実際には知りません。私のコードは、dispatch_async、dispatch_group_enterなどを利用しています。
更新:クラッシュの原因は、webserviceCall(以下のコードを参照)がonCompletionを呼び出さず、コードが再度実行されたときにエラーEXC_BAD_INSTRUCTIONが発生したためです。私はこれが実際にそうであることを確認しましたが、なぜこれを防ぐのか、どのように防ぐのかはわかりません。
コード:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_t group = dispatch_group_create();
for (...) {
if (...) {
dispatch_group_enter(group);
dispatch_async(queue, ^{
[self webserviceCall:url onCompletion:^{
dispatch_group_leave(group);
}];
});
}
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)));
dispatch_sync(queue, ^{
// call completion handler passed in by caller
});
});
スタックトレースから、dispatch_group_t
がまだロック中にリリースされた(dispatch_group_leave
を待っているであるため、EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
が発生しました。
あなたが見つけたものによると、これは何が起こったかでした:
dispatch_group_t group
が作成されました。 group
の保持カウント= 1。-[self webservice:onCompletion:]
はgroup
をキャプチャしました。 group
の保持カウント= 2。dispatch_async(...., ^{ dispatch_group_wait(group, ...) ... });
が再びgroup
をキャプチャしました。 group
の保持カウント= 3。group
がリリースされました。 group
の保持カウント= 2。dispatch_group_leave
は呼び出されませんでした。dispatch_group_wait
はタイムアウトしました。 dispatch_async
ブロックが完了しました。 group
がリリースされました。 group
の保持カウント= 1。-[self webservice:onCompletion:]
が再度呼び出されると、古いonCompletion
ブロックが新しいブロックに置き換えられました。そのため、古いgroup
がリリースされました。 group
の保持カウント= 0。group
は割り当て解除されました。その結果、EXC_BAD_INSTRUCTION
になりました。これを修正するには、-[self webservice:onCompletion:]
がonCompletion
ブロックを呼び出さなかった理由を見つけて修正することをお勧めします。その後、メソッドの次の呼び出しが前の呼び出しの後に行われることを確認します。終了しました。
前の呼び出しが終了したかどうかにかかわらず、メソッドを何度も呼び出せるようにする場合は、誰かがgroup
を保持してくれるかもしれません。
DISPATCH_TIME_FOREVER
に変更するか、すべての-[self webservice:onCompletion]
がonCompletion
ブロックを呼び出すまでに妥当な時間に変更できます。 dispatch_async(...)
のブロックがそれを保持します。group
などのコレクションにNSMutableArray
を追加できます。このアクション専用のクラスを作成する最良の方法だと思います。 webserviceを呼び出したい場合、クラスのオブジェクトを作成し、完了ブロックを渡してメソッドを呼び出しますオブジェクトを解放します。クラスには、dispatch_group_t
またはdispatch_semaphore_t
のivarがあります。
私の問題はIBOutlet
でしたが、インターフェイスビルダーに接続せず、Swiftファイルで使用しました。
この質問に私を導く別の問題がありました。これはおそらく、受け入れられた回答のオーバーリリースの問題よりも一般的です。
根本的な原因は、完了ブロックがtwiceと呼ばれることでした。これは、ネットワークハンドラーでのif/elseフォールスルーが不良であり、dispatch_group_leave
の呼び出しごとにdispatch_group_enter
が2回呼び出されるためです。
dispatch_group_enter(group);
[self badMethodThatCallsMULTIPLECompletions:^(NSString *completion) {
// this block is called multiple times
// one `enter` but multiple `leave`
dispatch_group_leave(group);
}];
count
を介したデバッグEXC_BAD_INSTRUCTION
では、デバッガーのdispatch_groupに引き続きアクセスできます。 dispatch_groupを印刷すると、以下が表示されます。
<OS_dispatch_group: group[0x60800008bf40] = { xrefcnt = 0x2, refcnt = 0x1, port = 0x0, count = -1, waiters = 0 }>
count = -1
が表示された場合、dispatch_groupが過剰に残っていることを示しています。必ずdispatch_enter
とdispatch_leave
のペアを一致させてください。
私の問題は、NSMutableDictionaryに保存したいオブジェクトを作成していたが、辞書を初期化したことがなかったことです。したがって、オブジェクトはガベージコレクションによって削除され、後で壊れていました。対話するオブジェクトへの少なくとも1つの強い参照があることを確認します。
EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
を取得するのに必要なのは、欠落しているreturn
ステートメントだけです。
確かに私の場合でした。
XCTestCaseのためにここに着きました。そこでは、no_testBackgroundAddingのように「no_」を前に付けて、ほとんどのテストを無効にしました。答えのほとんどがロックとスレッドに関係していることに気付いたとき、テストには、対応するwaitForExpectationsを持つXCTestExpectationのインスタンスがいくつか含まれていることがわかりました。それらはすべて無効なテストに含まれていましたが、明らかにXcodeは何らかのレベルでそれらを評価していました。
最終的に、@ propertyとして定義されているが@synthesizeが欠けているXCTestExpectationを見つけました。合成ディレクティブを追加すると、EXC_BAD_INSTRUCTIONは消えました。
私の場合:
PHImageRequestOptions *requestOptions = [PHImageRequestOptions new];
requestOptions.synchronous = NO;
Dispatch_groupでこれを行おうとしていました
私の問題は、init()にあったことです。おそらく「弱い自己」が彼を殺し、初期化は終了していなかったのでしょう。 initから移動して問題を解決しました。