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



在尝试了解更多关于核心数据和多线程的信息时,我编写了一个无法保存到核心数据的应用程序。第一部分背景在我的视图出现之前,我会创建托管上下文。这种情况发生在主线程:

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

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的情况下将数据保存到磁盘?

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

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

最新更新