1

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 を正しくマージしていると思います。

ヘルプ!ありがとうございました。

4

2 に答える 2

2

マルチスレッド Core Data 使用の主なルールは、管理対象オブジェクト コンテキストと永続ストアの「スレッド制限」です。つまり、スレッド ローカルの管理対象オブジェクト コンテキストを使用しても安全ですが、それらをあるスレッドから別のスレッドに渡すことはできません。その場合、独自のロックと同期を処理する必要があります。

セカンダリ スレッドでマネージド オブジェクト コンテキストを作成し、それをメイン スレッドに渡しているようです。私はそれを正しく理解していますか?もしそうなら、これはあなたがクラッシュしたことを説明することができます.

私が考えている1つの可能性はこれです:

  1. バックグラウンド スレッドの保存。通知が送信されます。

  2. メインスレッドがマージを開始します。

  3. マージの進行中に、バックグラウンド スレッドが別の保存を行います。

ポイント 3 で「保存」する代わりにprocessData、モックを別の状態にするメソッドで行う任意の操作である可能性があります。

この発生がクラッシュの原因であるかどうかは、既に用意されているログ トレースを使用して簡単に確認できるはずです。

于 2012-07-26T19:11:56.637 に答える
0

You aren't creating the context with a concurrency type, or merging changes through a parent child context relationship, so your likely problem is a threading issue. Contexts are not thread-safe, and must be accessed or mutated on the same thread that created them

于 2012-07-26T19:12:53.610 に答える