2

リモートサーバーからデータを取得してCoreDataSQLiteデータベースに保存するアプリを構築しています。メインスレッドがデータを消費している間に、バックグラウンドスレッドからデータをフェッチしています。これが私が使用している主なアプローチです。

  1. すべてのバックグラウンドスレッドには独自のがありNSManagedObjectContextます。
  2. persistentStoreCoordinatorコンテキスト間で共有されます。
  3. バックグラウンドスレッドでフェッチするたびに、挿入されたオブジェクトを保存し、ローカルコンテキストをリセットします。
  4. 通知を使用して、メインスレッドコンテキストとマージします。

私が経験している問題:

  • ランダムにNSZombie、バックグラウンドスレッドのsave:操作でメッセージなしでクラッシュが発生します(クラッシュブレークポイントが設定されます)。
  • 大量の重複データが生成されています。Webサービスのデータは確かに問題ないので、サーバー側では問題ありません。

ここにコードがあります。

AppDelegate

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    // Update data from remote server
    WebserviceDataModel *webservice = [[WebserviceDataModel alloc] init];
    webservice.managedObjectContext = self.managedObjectContext;
    [webservice startImport];
}

WebserviceDataModelでデータをフェッチして保存するバックグラウンドスレッド

- (void)startImport
{
    dispatch_queue_t downloadQueue = dispatch_queue_create("startImport in WebserviceDataModel", NULL);
    dispatch_async(downloadQueue, ^{
        NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
        moc.persistentStoreCoordinator = self.managedObjectContext.persistentStoreCoordinator;

        // get the remote data
        NSDictionary *lojas = [Loja allLojasFromRemoteServer];

        for (NSDictionary *lojaInfo in lojas) {
            Loja *loja __attribute__((unused)) = [Loja lojaWithRemoteData:lojaInfo inManagedObjectContext:moc];
        }

        if ([moc hasChanges]) {
            [moc save:nil];
            [moc reset];
        }
        [moc release];        

    });
    dispatch_release(downloadQueue);
}

オブジェクト作成用のNSManagedObjectメソッド:+ (Loja *)lojaWithRemoteData:inManagedContext:

+ (Loja *)lojaWithRemoteData:(NSDictionary *)remoteData inManagedObjectContext:(NSManagedObjectContext *)context
{
    Loja *loja = nil;

NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:@"Loja" inManagedObjectContext:context];
request.predicate = [NSPredicate predicateWithFormat:@"lojaId = %d", [[remoteData objectForKey:@"lojaId"] intValue]];

NSError *error = nil;
loja = [[context executeFetchRequest:request error:&error] lastObject];
[request release];

if (!error && !loja) {
        // create the record
        loja = [NSEntityDescription insertNewObjectForEntityForName:@"Loja" inManagedObjectContext:context];

        loja.lojaId = [remoteData objectForKey:@"lojaId"];
        loja.email = [remoteData objectForKey:@"email"];
        loja.facebook = [remoteData objectForKey:@"facebook"];
        // ... and others...
    }

    return loja;
}

WebserviceDataModelのNSManagedObjectContextDidSaveNotificationへのサブスクリプション

- (id)init
{
    self = [super init];
    if (self) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextChanged:) name:NSManagedObjectContextDidSaveNotification object:nil];
    }
    return self;
}

contextChanged:WebserviceDataModelのメソッド

- (void)contextChanged:(NSNotification*)notification
{
    if ([notification object] == self.managedObjectContext) return;

    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(contextChanged:) withObject:notification waitUntilDone:YES];
        return;
    }
    [[self managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
}
4

2 に答える 2

1

まあ、私はそれを理解していました。そして今では魅力のように機能しています。マルチスレッドコンテキストの実装は正しいです。問題は、applicationDidBecomeActive:マイアプリが機能のリストにCoreLocationFenceを使用することでした。アプリが初めて起動すると、CoreLocationフレームワークは、アプリがユーザーの場所を使用していることを示すアラートメッセージをユーザーに表示します...このアラートがapplicationDidBecomeActive:再度呼び出され、2つの更新の波が同時に発生します。WebserviceDataModelをプロパティに移動し、実行されているかどうかを知るためのフラグを実装しました。それでおしまい

最終的な改良のために、マージポリシーをに変更しましたNSMergeByPropertyStoreTrumpMergePolicy。これにより、サーバー側のデータ(メモリ内)がローカルストアに優先されます。

于 2012-05-15T16:51:16.647 に答える
0

詳細がなければ、何が起こっているのかを理解するのは非常に困難です。

このように、まず、ブレークポイントを設定し、アプリケーションフローに従ってください。たぶん、アプリがクラッシュするポイントを見つけることができます。

lojaIdそれでは、それがスカラー値であると確信していますか?新しいエンティティを作成するときは、次のように記述します。

loja.lojaId = [remoteData objectForKey:@"lojaId"];

おそらくこれはエラーである可能性があるので、次の述語を試してみます。

[NSPredicate predicateWithFormat:@"lojaId == %@", [remoteData objectForKey:@"lojaId"]];

最後に、保存を行うときは、NSErrorオブジェクトをログに記録してみてください。多分あなたはあなたのクラッシュの原因を見つけることができます。

if ([moc hasChanges]) {

    NSError* error = nil;
    [moc save:&error];

    if(error) {

        NSLog(@"Error during save: %@\n%@", [error localizedDescription], [error userInfo]);
        abort(); // only for debug purposes
    }
    [moc reset];
}

それが役に立てば幸い。

于 2012-05-08T15:27:28.487 に答える