[英]NSFetchRequest on didEnterRegion concurrency issue
我是Core Data的新手,我研究了许多与Cored Data相关的问题,但是我无法真正完全掌握Core Data和并发的整个概念。 似乎有一种方法可以做到,但是近年来它发生了变化,现在很难过滤出哪些信息是最新的,哪些不是最新的。 我正在开发一个小型应用程序,该应用程序从Web服务导入商店数据,将其呈现给用户,并监视用户何时进入特定商店的区域。 我正在使用两个NSManagedObjectContext
,它们是CoreDataHelper
类的一部分。 这是作为单例实现的CoreDataHelper
的init
:
- (id)init {
self = [super init];
if (!self) {return nil;}
_model = [NSManagedObjectModel mergedModelFromBundles:nil];
_coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:_model];
_parentContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_parentContext performBlockAndWait:^{
[_parentContext setPersistentStoreCoordinator:_coordinator];
[_parentContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
}];
_context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_context setParentContext:_parentContext];
[_context setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
return self;
}
我使用_ context
将数据呈现给用户,并使用_parentContext
在后台导入和同步数据。 我的问题是,当用户进入区域并didEnterRegion
,我使用以下方法检索该区域中商店的NSManagedObject
- (Shop*)findShopWithID:(NSString*)shopID
{
NSArray* fetchedObjects;
NSManagedObjectContext* context = [self getBackgroundManagedObectContext];
if(context==nil)
[self sendRemoteLog:@"Context is nil"];
NSFetchRequest* fetch = [[NSFetchRequest alloc] init];
NSEntityDescription* entityDescription = [NSEntityDescription entityForName:@"Shop" inManagedObjectContext:context];
[fetch setEntity:entityDescription];
[fetch setPredicate:[NSPredicate predicateWithFormat:@"shopID==%@", shopID]];
NSError* error = nil;
fetchedObjects = [context executeFetchRequest:fetch error:&error];
if ([fetchedObjects count] == 1)
return [fetchedObjects objectAtIndex:0];
else
{
[self sendRemoteLog:[NSString stringWithFormat:@"No object found for ID %@",shopID]];
return nil;
}
}
- (NSManagedObjectContext*)getBackgroundManagedObectContext
{
CoreDataHelper* cdh = [(AppDelegate*)[[UIApplication sharedApplication] delegate] cdh];
return [cdh backgroundSaveContext];
}
但是有时,并非总是如此,即使该请求100%存在,提取请求也找不到具有给定ID的商店。 该问题仅在某些时候发生的事实使我得出结论,这与并发性有关。 另外,我认为这可能与以下事实有关:我在此处使用相同的parentContext
对象来执行获取操作,并且还异步地从Web服务中导入数据。 我可能应该更改它,但是我认为这不是唯一的问题。
编辑:这是CoreDataHelper
保存机制
- (void)saveContext
{
if ([_context hasChanges]) {
NSError* error = nil;
if ([_context save:&error]) {
NSLog(@"_context SAVED changes to persistent store");
}
else {
NSLog(@"Failed to save _context: %@", error);
}
}
else {
NSLog(@"SKIPPED _context save, there are no changes!");
}
}
- (void)backgroundSaveContext {
// First, save the child context in the foreground (fast, all in memory)
[self saveContext];
// Then, save the parent context.
[_parentContext performBlock:^{
if ([_parentContext hasChanges]) {
NSError *error = nil;
if ([_parentContext save:&error]) {
NSLog(@"_parentContext SAVED changes to persistent store");
}
else {
NSLog(@"_parentContext FAILED to save: %@", error);
}
}
else {
NSLog(@"_parentContext SKIPPED saving as there are no changes");
}
}];
}
您的猜测很可能是正确的。 您不会通过将一种上下文用于UI表示而将一种上下文用于后台提取来遵循自己的模式。
因此,您的第一个操作步骤是使用适当的背景上下文进行导入,并使用子上下文进行显示。
不过,我认为将父上下文作为主要的UI上下文并使用子上下文在后台进行操作会更加合乎逻辑。 然后,子上下文的“保存”会将更改推送到UI。
另外,请确保通过NSNotificationCenter
订阅NSManagedObjectContextDidSaveNotification
并正确合并更改,然后更新用户界面来处理更改。
经过大量的调试和耐心,我设法查明了问题所在。 它与并发无关,在我的谓语中是一个愚蠢的错误。 我正在使用以下代码行:
[fetch setPredicate:[NSPredicate predicateWithFormat:@"shopID==%@", shopID]];
我错误地将shopID用作NSString
而在我的核心数据模型和NSManagedObject
它是一个NSNumber
。 我重新考虑了将shopID更改为存储为NSString
,现在它可以正常工作。 但是我仍然无法解释为什么它有时会工作,有时却无法工作...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.