2

我正在构建一个从远程服务器提取数据并将它们存储在 CoreData SQLite 数据库中的应用程序。我正在从后台线程获取数据,而主线程正在使用它。以下是我正在使用的主要方法。

  1. 所有后台线程都有自己的NSManagedObjectContext.
  2. persistentStoreCoordinator是在上下文之间共享的。
  3. 在后台线程的每次提取中,我保存:插入的对象并重置:本地上下文。
  4. 使用通知我与主线程上下文进行合并。

我遇到的问题:

  • 随机我NSZombie在后台线程保存时遇到没有消息的崩溃(并且设置了崩溃断点):操作。
  • 有很多重复的数据正在生成。网络服务数据肯定没问题,所以服务器端没有问题。

这里是代码。

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;
}

WebserviceDataModel 上的 contextChanged: 方法

- (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:我的应用程序使用 CoreLocation Fence 来获取功能列表。当应用程序第一次启动时,CoreLocation 框架会向用户显示一条警报消息,指出该应用程序使用用户位置...该警报applicationDidBecomeActive:再次调用,创建两个并发更新波。只需将我的 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 回答