何らかの理由でdispatch_asyncとCoreDataを一緒に使用すると、アプリが完全にフリーズしますが、クラッシュしません。
症状
ユーザーインターフェイスが応答しません。アプリはクラッシュしません。
アプリは[[UIAccelermeter sharedAccelerometer] setDelegate: self]
、加速度計の値を継続的に取得するために使用します。" accelerometer:didAccelerate:
"コールバックはメインスレッドで実行されます。この問題が発生すると、コールバックは実行されなくなります。
したがって、メインスレッドは「フリーズ」しているように見えます。
コンテクスト
NSURLConnection
アプリでHTTPリクエストをサーバーに送信するために使用しています。応答データが処理されるconnectionDidFinishLoadingでは、実行に約2秒かかり、メインスレッドで実行されるため、この2秒間はユーザーインターフェイスが応答しなくなります。
いくつかの調査の結果、gcd/dispatch_async
この状況でデータ処理をバックグラウンドで実行するのが最善の解決策のようです。そこで、でconnectionDidFinishLoading
、次のgcd呼び出しを追加しました。
dispatch_queue_t queue;
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[self handleTheData: connection];
});
これは正常に機能する場合もありますが、アプリがランダムにフリーズする可能性があります-同じコードが使用されていても、Core Dataストレージに同じデータがあり、テストを繰り返すと、X%の確率で症状が観察されることがわかります。問題が発生する可能性は、以下でさらに説明するいくつかの要因の影響を受けます。
handleTheData()は何をしますか?
NSURLConnection
JSON形式の文字列であるからの応答データを調べます。文字列はエンティティの3つのリストであり、各エンティティは数値の配列の配列です。したがって、JSON形式の文字列をカスタムオブジェクトの3 NSMutableArrayに変換するコードがあります。ここで、各オブジェクトはサブクラス化されたNSManagedObjectです。
変換中、これらのオブジェクトは永続的に保存される場合とされない場合があるという点で「一時的」であるため、次のように「nil」NSManagedObjectContextで作成されます。
SampleEntity *newEntity=
(SampleEntity *) [[NSManagedObject alloc] initWithEntity:entity
insertIntoManagedObjectContext:nil
];
このように、それらはメインコンテキストと混合されません。
次に、これらの新しいオブジェクトがデータストア内の既存のエンティティと比較されます。そのため、特定の新しいエンティティがすでに存在するかどうかを確認するためにデータストアから繰り返しフェッチするコードがいくつかあり、新しいエンティティのデータに応じて、新しいエンティティをデータストアに保存したり、既存のエンティティを更新したりできます。
[managedObjectContext save:&error]は、変更が永続的に保存されるようにするために頻繁に呼び出されます。
他にバックグラウンドで実行されているものはありますか?
症状のセクションで説明したように、Accelerometerは、[[UIAccelermeter sharedAccelerometer] setDelegate:self]を使用してメインスレッドのバックグラウンドで実行されます。
AudioUnitsは、サウンドを生成するためにバックグラウンドで(Appleドキュメントによると別の「優先」スレッドで)実行されるようにも設定されています。
問題の原因に関する調査結果
問題が発生する可能性は、次の要因の影響を受けることがわかりました。
- NSLog(@ "fetchedResult:%@"、fetchedResult)などのデバッグコードがコメント化されている場合、問題が発生する可能性は低くなります(ただし、引き続き発生します)。
- 加速度計を使用しない場合、問題が発生することはほとんどありません(ただし、限られた数のテストを行ったため、100%確実ではありません)。
- 加速度計を使用する場合、問題が発生する可能性は約50/50です。
- [sampleEntity setXYZ ..]などのコアデータコードの一部がコメント化されている場合、加速度計を使用しても問題が発生することはほとんどありません。
第二号
また、メインスレッドがフリーズしていなくても、別の問題が発生します。データ処理コードは、エラーをスローせずに途中で終了します。
- つまり、データ処理は部分的にのみ実行され、エラーはスローされず、メインスレッドは引き続き正常に実行されます。
- この2番目の問題は、主な問題が発生しない場合は常に頻繁に発生します(常にではありません)。
面白い問題ですね。:)
編集
- 2013年3月1日-以前は問題の原因はiOS6だと思っていました。申し訳ありませんが、それは完全に間違っていました。新しい発見を反映するために説明を変更しました。