核心数据管理上下文不在多线程环境中保存

时间:2014-01-02 05:32:44

标签: ios objective-c multithreading core-data

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

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

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

        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分钟才能检索 但是,我希望视图在此期间加载任何我 有。上述方法阻止了我 尽管有明确的调用,但能够保存到上下文 refreshClientInformation中的[self.managedContext save:&error] 每次客户端数据更新后。 refreshPersonalClientInformations在“客户端获取”线程队列中执行。

- (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中运行save:

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

但这没有用,没有发现错误

作为一项实验,当我的视图出现时,我将线程调用更改为此 核心数据最终得到保存,问题是我的视图被阻止了,我必须这样做 在检索数据时等待很长时间。另外,当我最终说出来时,我的意思是在我拨打[self.managedContext save:nil]之后不会发生这种情况,但是几分钟之后:

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

有关如何在后台线程中检索我的信息的任何建议 并且能够将数据保存到磁盘,同时我仍然可以访问我的UI吗?

1 个答案:

答案 0 :(得分:1)

您最好创建子上下文以在后台执行所有操作:

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