SQLLite コア データ データベースを使用する iOS アプリを開発しています。アプリは、Web サービスからデータを取得してデータベースに書き込むバックグラウンド スレッドで同期ループを実行します。これが発生している間、フォアグラウンド (UI) スレッドは続行され、ユーザーはデータベースに対して検索を実行できます。
アプリのストレス テストを行っていますが、アプリがクラッシュしています。バックグラウンド同期タスクの実行中に、フォアグラウンドでデータベースに対して検索を実行しています。データベースには約 10,000 のレコードがあるため、巨大ではありません。
バックグラウンド スレッドは NSOperation を使用して作成され、NSOperation のメイン メソッドで NSManagedObjectContext を作成します。
フォアグラウンド スレッドは、appDelegate 内で初期化された (および appDelegate によって使用可能にされた) 別の NSManagedObjectContext オブジェクトを使用します。
バックグラウンド同期スレッドは、一度に 1000 レコードをデータベースに書き込み、次にその managedobjectcontext が保存を実行します。
NSOperation のメイン メソッドは次のようになります。
-(void) main {
NSDictionary* dictionary = [ HPSJSON getDictionaryFromData:_data ];
// NEED to create the MOC here and pass to the methods.
NSManagedObjectContext* moc = [[NSManagedObjectContext alloc] init];
[moc setUndoManager:nil];
//[moc setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
//[moc setMergePolicy:NSOverwriteMergePolicy];
[moc setPersistentStoreCoordinator:getApp().persistentStoreCoordinator];
if (dictionary==nil){
[ getApp().operationManager performSelectorOnMainThread: [ self getFailedFunction ] withObject:nil waitUntilDone:NO ];
return;
}
NSString* rc = [self processData: dictionary andMOC:moc ]; // Writes lots of records to the db and saves the moc
// performSelectorOnMainThread invokes a method of the receiver on the main thread using the default mode.
// i.e. call the method within HPSOperationManager as specified by the getSuccessFunction of the specialised sub-class
[ getApp().operationManager performSelectorOnMainThread: [ self getSuccessFunction ] withObject:rc waitUntilDone:NO ];
}
processData メソッド (main によって呼び出される) には多くのコードが含まれていますが、保存を行うスニペットを次に示します。
@try {
NSLog(@"HPSDbOperation%@ about to save Indexes moc",objectName);
if (rcHappy==YES)
{
// register for the moc save notification - this is so that other MOCs can be told to merge the changes
[[NSNotificationCenter defaultCenter]
addObserver:getApp()
selector:@selector(handleDidSaveNotification:)
name:NSManagedObjectContextDidSaveNotification
object:moc];
NSError* error = nil;
if ([moc save:&error] == YES)
{
NSLog(@"HPSDbOperation%@ Indexes SAVED",objectName);
}else {
NSLog(@"HPSDbOperation%@ Indexes NOT saved ",objectName);
}
// unregister from notification
[[NSNotificationCenter defaultCenter]
removeObserver:getApp()
name:NSManagedObjectContextDidSaveNotification
object:moc];
}
}
@catch (NSException * e) {
NSLog(@"HPSDbOperationBase save Indexes Exception: %@", e);
rcHappy=NO;
}
appDelegate には、ManagedObjectContext マージを処理する次のメソッドが含まれています。
- (void)handleDidSaveNotification:(NSNotification*) note
{
@try {
NSLog(@"appDelegate handleDidSaveNotification about to run");
[__managedObjectContext mergeChangesFromContextDidSaveNotification:note];
NSLog(@"appDelegate handleDidSaveNotification did run");
}
@catch (NSException * e) {
NSLog(@"appDelegate handleDidSaveNotification Exception: %@", e);
}
}
同期を実行した後、連続して検索を実行してフォアグラウンド UI スレッドに負荷をかけます。同期と検索の 1 ~ 3 分後に、アプリがクラッシュします。私のtry-catch構造のどれにも引っかからないようです。Xcode のクラッシュ ログには、次のように表示されます。
libobjc.A.dylib`objc_msgSend:
0x34d92f68: teq.w r0, #0
0x34d92f6c: beq 0x34d92faa ; objc_msgSend + 66
0x34d92f6e: push.w {r3, r4}
0x34d92f72: ldr r4, [r0] <-- exception here Thread1 EXC_BAD_ACCESS (Code=1)
0x34d92f74: lsr.w r9, r1, #2
0x34d92f78: ldr r3, [r4, #8]
0x34d92f7a: add.w r3, r3, #8
0x34d92f7e: ldr r12, [r3, #-8]
0x34d92f82: and.w r9, r9, r12
0x34d92f86: ldr.w r4, [r3, r9, lsl #2]
0x34d92f8a: teq.w r4, #0
0x34d92f8e: add.w r9, r9, #1
0x34d92f92: beq 0x34d92fa6 ; objc_msgSend + 62
0x34d92f94: ldr.w r12, [r4]
0x34d92f98: teq.w r1, r12
0x34d92f9c: bne 0x34d9317e ; objc_msgSendSuper_stret + 34
0x34d92f9e: ldr.w r12, [r4, #8]
0x34d92fa2: pop {r3, r4}
0x34d92fa4: bx r12
0x34d92fa6: pop {r3, r4}
0x34d92fa8: b 0x34d92fb0 ; objc_msgSend_uncached
0x34d92faa: mov.w r1, #0
0x34d92fae: bx lr
私はiOSとobjective-cとcore-dataに非常に慣れていません。この問題をどのように進めるかについて、私は少し立ち往生しています。アプリのどこで、なぜ問題が発生しているのかを正確に知るにはどうすればよいですか? なぜクラッシュするのか、誰にも分かりますか?コア データのマルチスレッドについて少し読んだことがありますが、NSOperation のメイン メソッドで MOC を作成することでガイドラインに従っており、handleDidSaveNotification を使用することで MOC を正しくマージしていると思います。
ヘルプ!ありがとうございました。