web-dev-qa-db-ja.com

dispatch_semaphore_disposeのEXC_BAD_INSTRUCTION(コード= EXC_I386_INVOP、サブコード= 0x0)

Dispatch_semaphore_disposeでEXC_BAD_INSTRUCTION(code = EXC_I386_INVOP、subcode = 0x0)を取得していますが、これの根本原因を追跡する方法を実際には知りません。私のコードは、dispatch_async、dispatch_group_enterなどを利用しています。

更新:クラッシュの原因は、webserviceCall(以下のコードを参照)がonCompletionを呼び出さず、コードが再度実行されたときにエラーEXC_BAD_INSTRUCTIONが発生したためです。私はこれが実際にそうであることを確認しましたが、なぜこれを防ぐのか、どのように防ぐのかはわかりません。

enter image description here

コード:

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
    });
});
40
Boon

スタックトレースから、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を保持してくれるかもしれません。

  • タイムアウトを2秒からDISPATCH_TIME_FOREVERに変更するか、すべての-[self webservice:onCompletion]onCompletionブロックを呼び出すまでに妥当な時間に変更できます。 dispatch_async(...)のブロックがそれを保持します。
    または
  • groupなどのコレクションにNSMutableArrayを追加できます。

このアクション専用のクラスを作成する最良の方法だと思います。 webserviceを呼び出したい場合、クラスのオブジェクトを作成し、完了ブロックを渡してメソッドを呼び出しますオブジェクトを解放します。クラスには、dispatch_group_tまたはdispatch_semaphore_tのivarがあります。

42
3329

私の問題はIBOutletでしたが、インターフェイスビルダーに接続せず、Swiftファイルで使用しました。

7
Mrugesh Tank

この質問に私を導く別の問題がありました。これはおそらく、受け入れられた回答のオーバーリリースの問題よりも一般的です。

根本的な原因は、完了ブロックが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);
}];

Dispatch_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_enterdispatch_leaveのペアを一致させてください。

6
pkamb

私の問題は、NSMutableDictionaryに保存したいオブジェクトを作成していたが、辞書を初期化したことがなかったことです。したがって、オブジェクトはガベージコレクションによって削除され、後で壊れていました。対話するオブジェクトへの少なくとも1つの強い参照があることを確認します。

4
mihai

EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)を取得するのに必要なのは、欠落しているreturnステートメントだけです。

確かに私の場合でした。

2
Gabriel

XCTestCaseのためにここに着きました。そこでは、no_testBackgroundAddingのように「no_」を前に付けて、ほとんどのテストを無効にしました。答えのほとんどがロックとスレッドに関係していることに気付いたとき、テストには、対応するwaitForExpectationsを持つXCTestExpectationのインスタンスがいくつか含まれていることがわかりました。それらはすべて無効なテストに含まれていましたが、明らかにXcodeは何らかのレベルでそれらを評価していました。

最終的に、@ propertyとして定義されているが@synthesizeが欠けているXCTestExpectationを見つけました。合成ディレクティブを追加すると、EXC_BAD_INSTRUCTIONは消えました。

1
Elise van Looij

私の場合:

PHImageRequestOptions *requestOptions = [PHImageRequestOptions new];
requestOptions.synchronous            = NO;

Dispatch_groupでこれを行おうとしていました

私の問題は、init()にあったことです。おそらく「弱い自己」が彼を殺し、初期化は終了していなかったのでしょう。 initから移動して問題を解決しました。

0