ios - 核心数据托管上下文未保存在多线程环境中

标签 ios objective-c multithreading core-data

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

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

当我的 View 出现时,我使用这种方法从客户端获取数据。这 我这样做的原因是因为我不想作为我的客户阻止我的观点 正在刷新:

        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 分钟来检索 信息,但是,我希望同时加载 View 有。上述方法阻止了我 尽管明确调用了,但仍能保存到上下文中 [self.managedContext save:&error] 在 refreshClientInformation 每次客户端数据更新后。 refreshPersonalClientInformations 在“Client Fetch”线程队列中执行。

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

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

但这没有用,没有检测到错误

作为实验,当我的 View 出现并且我的 核心数据最终得到保存,问题是我的 View 被阻止了,我必须 在检索数据时等待很长时间。另外,当我说最终时,我的意思是它不会在我调用 [self.managementContext 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 的同时将数据保存到磁盘?

最佳答案

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

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

关于ios - 核心数据托管上下文未保存在多线程环境中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20876922/

相关文章:

ios - 新屏幕尺寸的动画 View 问题

iphone - 收到通知后,UILabel 文本不更新

ios - 以 %i 形式获取 UISwitch 的值

c# - IOException 被 ReaderWriteLockSlim 抛出?

Java并发: CopyOnWriteArrayList behavior

iphone - 如何使 UITableView 中的单元格可移动但不可删除?

IOs 自动布局顶部位置

ios - base64 字符串到 iOS 中的音频 mp3?

ios - 像 Facebook 这样的集成图像选择器

java - 用于查看 Java 进程中的线程的实用程序