核心数据管理上下文不保存在多线程环境中 [英] Core Data Managed Context not saving in multi-threaded environment

查看:248
本文介绍了核心数据管理上下文不保存在多线程环境中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在尝试了解有关Core Data和多线程的更多信息时,我写了一个无法保存到核心数据的应用程序。首先一些
的背景。在我的视图出现之前,我创建托管上下文。这发生在
主线程:

While trying to learn more about Core Data and multithreading, I wrote an app that fails to save to core data. First some background. Before my view appears, I create managed context. This happens in the main thread:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    if (!self.managedObjectContext) [self useDocument];

}

- (void)useDocument
{
    NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    url = [url URLByAppendingPathComponent:MY_DOCUMENT];
    UIManagedDocument *document = [[UIManagedDocument alloc] initWithFileURL:url];

    if (![[NSFileManager defaultManager] fileExistsAtPath:[url path]]) {
        [document saveToURL:url
           forSaveOperation:UIDocumentSaveForCreating
          completionHandler:^(BOOL success) {
              if (success) {
                  NSLog(@"@@@AMRO--->managedObjectContext has been setup");
                  self.managedObjectContext = document.managedObjectContext;
                  [self refresh];
              }
          }];
    } else if (document.documentState == UIDocumentStateClosed) {
        [document openWithCompletionHandler:^(BOOL success) {
            if (success) {
                NSLog(@"@@@AMRO--->managedObjectContext has been opened");
                self.managedObjectContext = document.managedObjectContext;
                [self refresh];
            }
        }];
    } else {
        NSLog(@"@@@AMRO--->managedObjectContext has been set");
        self.managedObjectContext = document.managedObjectContext;
    }
}

当我的视图出现时,我从客户端提取数据使用这种方法。
的原因,我这样做是因为我不想我的视图被阻止,因为我的客户端
刷新发生:

When my view appears, I fetch data from a client using this approach. The reason I do this is because I do not want my view to be blocked as my client refresh is happening:

        dispatch_queue_t fetchQ = dispatch_queue_create("Client Fetch", NULL);
        dispatch_async(fetchQ, ^{
            [self.managedObjectContext performBlock:^{
               dispatch_async(dispatch_get_main_queue(), ^{
                    [self reload];
                    dispatch_async(fetchQ, ^{
                        [self refreshClientInformation];
                    });
                });
            }];
        });

调用 refreshClientInformation ,大约需要2分钟来检索
信息,但是,我希望视图加载与任何I
有同时。以上方法阻止我
能够保存到上下文,尽管在refreshClientInformation中显式调用
[self.managedContext save:& error]
每次客户端数据更新后。 refreshPersonalClientInformations 在客户端提取线程队列中执行。

The call to refreshClientInformation, takes about 2 minutes to retrieve information, however, I want the view to load in the meantime with whatever I have. The above approach prevents me from being able to save to context despite the explicit call to [self.managedContext save:&error] in refreshClientInformation after each client data update. refreshPersonalClientInformations executes in the "Client Fetch" thread queue.

- (void) refreshPersonalClientInformation
{
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"CLIENT"];
    request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"stationId" ascending:YES]];
    NSDate *twoHoursAgo = [NSDate dateWithTimeIntervalSinceNow:-2*60*60];
    request.predicate = [NSPredicate predicateWithFormat:@"conditionsUpdated < %@", twoHoursAgo];
    NSError *error = nil;

    int counter = 0;

    // Execute the fetch
    NSArray *matches = [self.managedObjectContext executeFetchRequest:request error:&error];

    // Check what happened in the fetch
    if (!matches) {  // nil means fetch failed;
        NSLog(@"ERROR: Fetch failed to find CLIENT %@ in database", DEFAULT);
    } else if (![matches count]) { // none found, so lets retrieve it below
        return;
    } else {
        for (CLIENT *client in matches) {
            NSDictionary *clientDict = [ClientFetcher clientInfo:client.clientId];

            client.name = [clientDict[NAME] description];

            client.age = [clientDict[AGE] description];
            client.updated = [NSDate date];
            if (![self.managedObjectContext save:&error]) {
                NSLog(@"@@@@@@@@@@@@@@@@@@@@@@@@@@AMRO Unresolved error %@, %@", error, [error userInfo]);
            }

            //Have to rate limit requests at no more than 10 per minute.
            NSLog(@"Sleeping for 6s");
            sleep(6);
            NSLog(@"Done sleeping for 6s");
        }
    }
}



我甚至试图运行通过执行以下操作保存在refreshPersonalClientInfo中:

I even tried to run the save in refreshPersonalClientInfo by doing this:

 dispatch_async(dispatch_get_main_queue(), ^{
     [self.managedObjectContext save:nil];
 });

但这没有效果,没有检测到错误

But that did not work, no errors were detected

作为一个实验,我改变了线程调用这个当我的视图出现和我的
核心数据最终得到保存,问题是我的视图被阻止,我得到
等待很长时间而数据正在检索。另外,当我最后说,我的意思是,当我调用 [self.managedContext save:nil] 后不会发生,但几分钟后:

As an experiment I changed the thread call to this when my view appears and my core data eventually gets saved, the problem is that my view is blocked and I have to wait a long time while the data is getting retrieved. Also when I say eventually, I mean that it does not happen after I call [self.managedContext save:nil], but after a few minutes:

        dispatch_queue_t fetchQ = dispatch_queue_create("Client Fetch", NULL);
        dispatch_async(fetchQ, ^{
            [self.managedObjectContext performBlock:^{
                [self refreshPersonalClientInformation];
                dispatch_async(dispatch_get_main_queue(), ^{
                    [self reload];
                });
            }];
        });

有关如何在后台线程中检索我的信息的任何建议

Any suggestions on how I should retrieve my informaion in a background thread and be able to save the data to disk while I still leave my UI accessible?

推荐答案

您最好创建一个子上下文来执行一切在后台:

You're better to create a child context to perform everything in the background:

dispatch_queue_t fetchQ = dispatch_queue_create("Client Fetch", NULL);
    dispatch_async(fetchQ, ^{
        NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        context.parentContext = self.managedObjectContext;
        [context performBlock:^{
            [self refreshPersonalClientInformation];
            dispatch_async(dispatch_get_main_queue(), ^{
                [self reload];
            });
         }];
    });

这篇关于核心数据管理上下文不保存在多线程环境中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆