1

次のコードは、iOS シミュレーターでクラッシュを引き起こします。

- (void)viewDidLoad
{
    [super viewDidLoad];
    dispatch_async(dispatch_get_main_queue(), ^{
        NSDate *sleepStart = [NSDate date];
        while ([sleepStart timeIntervalSinceNow] > -300) {

        }
    });    
}

更新: この問題は、バックグラウンド スレッドでも発生します

以下のコードもバグがあります。

- (void)viewDidLoad
{
    [super viewDidLoad];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSDate *sleepStart = [NSDate date];
        while ([sleepStart timeIntervalSinceNow] > -300) {

        }
    });    
}

それで全部です。これを任意のビュー コントローラーに貼り付け、シミュレーターでアプリを正確に 4 分 15 秒間実行すると、クラッシュします。

クラッシュは私が今まで見たことがないタイプです。「EXC_???(11)」です。奇妙なことに、クラッシュ後に [続行] ボタンを押すと、通常どおり続行されます。

なぜこれがクラッシュするのですか?この動作を引き起こさずに長いタスクをキューに送信するにはどうすればよいですか?

これまでに私が試したことがいくつかありますが、謎にまったく光を当てていません:

  • 独自のディスパッチ キューの作成(バックグラウンド スレッドのディスパッチ キューを含む)
  • GCD の代わりに NSBlockOperation を使用する (依然としてクラッシュする)
  • 挿入は眠る。短いスリープ (5 秒程度) は、スリープ時までにクラッシュを遅らせるようです。より長い睡眠はより多くのことをするようです. したがって、5 秒間 1 回スリープすると、4 分 15 秒ではなく 4 分 20 秒でクラッシュします。1 回 60 秒間スリープすると、クラッシュするまでに約 10 分かかりますが、最終的には発生します。この手がかりは重要なようですが、それが何を意味するのかわかりません。

アップデート #1

この問題は、GDB ではなく、LLDB でのみ再現されます。

4

2 に答える 2

5

メイン スレッドで長時間の操作を実行することはできません。ブロックを別のスレッドにディスパッチし、ブロックの最後にメイン スレッドにディスパッチする必要があります (必要な場合)。

を使用dispatch_async(dispatch_get_main_queue(),^{});すると、近い将来、メイン スレッドで操作が実行 (ブロック) されます。

メインスレッドはタイマーによって保護されています。イベントへの応答を停止すると、iOS はそれを強制終了します。これは意図的なものです: メインスレッドで重いものを持ち上げないでください!

iOS の許容範囲は通常4 分よりもはるかに短いですが、デバッグ中はさらに長くなります。シミュレーターには独自のルールがあります。

起動時は数秒です。ただし、メイン スレッドで 1 秒以上かかる操作は行わないでください。それは、直接的なユーザー アクション (ユーザーが何かをタップするなど) に応答する場合のみです。メイン スレッドで作業を行うと、iOS UI の応答性が低下し、ガラスのように滑らかではなくぎくしゃくしたものになります。

アプリが OSX のメイン スレッドのイベントに応答しなくなると、ビーチボールになります。アプリが iOS のメイン スレッドのイベントに応答しなくなり、iOS ウォッチドッグがそれを取り出して撃ちます。

TechNote TN2151から:

例外コード 0x8badf00d は、ウォッチドッグ タイムアウトが発生したためにアプリケーションが iOS によって終了されたことを示します。アプリケーションの起動、終了、またはシステム イベントへの応答に時間がかかりすぎました。これの一般的な原因の 1 つは、メイン スレッドで同期ネットワークを実行していることです。スレッド 0 での操作は何であれ、メイン スレッドをブロックしないように、バックグラウンド スレッドに移動するか、別の方法で処理する必要があります。

一般的に、パターンは次のとおりです。

- (IBAction)tappedWhatever:(id)sender {
    // visually start operation
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
        // do work here, off main thread
        // (you can't update the UI here)
        dispatch_async(dispatch_get_main_queue(),^{
            // show progress in UI
        });
        // more heavy lifting
        dispatch_async(dispatch_get_main_queue(),^{
            // update UI to show operation complete and move to next step
        });
    });
};

(Apple のドキュメントにこれについてのより良い説明があったと断言できたのですが、今は見つかりません。誰かいますか?)

于 2013-03-04T03:44:35.773 に答える
3

アップルのエンジニア より:

表示されている「クラッシュ」は、実際にはクラッシュではなく、アプリケーションが長時間にわたって CPU 時間を使いすぎた場合のログです。存在する理由は、それをしないように伝えるためであり、CPU 時間が費やされている場所です。本番環境では、ログは単純に記録され、プロセスは続行されます。しかし、これらのタイプのログを避けることができれば、そのほうがよいでしょう。

以降:

バグレポートを提出する必要があります。lldb は、デフォルトでそのトラップを無視できるほど賢くなければなりません。

于 2013-03-05T03:50:37.497 に答える