Core Data后台上下文插入数据后主线程查询无结果求助
嘿,我帮你排查了下代码里的问题,核心原因是Core Data上下文的保存链没打通,而且主线程上下文没有同步后台插入的数据,咱们一步步理清楚:
问题1:后台上下文的变更没有同步到父上下文并持久化
你创建的bgContext把saveObjectContext作为父上下文,当你调用[bgContext save:&error]时,只是把数据从后台上下文推到了父上下文(saveObjectContext),但父上下文并没有把这些变更保存到磁盘,也没有通知主线程的上下文(你查询用的_managedObjectContext)。这就导致主线程上下文完全不知道后台插入了新数据。
问题2:主线程上下文不会自动刷新数据
就算父上下文保存了,主线程的NSManagedObjectContext也不会自动感知到其他上下文的变更,需要手动触发刷新或者通过通知合并变更。
修复方案
第一步:完善后台插入的保存逻辑
在后台上下文保存成功后,必须切换到父上下文的队列(如果父上下文是主线程上下文,一定要在主线程执行保存)去保存父上下文,这样变更才会持久化。同时优化批量插入的保存时机(不需要循环里每次都save):
- (void)insertNameAndSurnameInDataBase:(NSString *)name surname:(NSString *)surname numbers:(NSArray *)numbers labels:(NSArray *)labels { AppDelegate *appDel=(AppDelegate *)[UIApplication sharedApplication].delegate; NSManagedObjectContext *saveObjectContext = [appDel saveManagedObjectContext]; NSManagedObjectContext *bgContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType]; bgContext.parentContext = saveObjectContext; [bgContext performBlockAndWait:^{ NSError *error; NameAndSurname *nameAndSurname = [NSEntityDescription insertNewObjectForEntityForName:@"NameAndSurname" inManagedObjectContext:bgContext]; NSString *nSurname = [NSString stringWithFormat:@"%@ %@",name,surname]; if (nSurname.length == 0) { nameAndSurname.nameSurname = [numbers firstObject]; } else { nameAndSurname.nameSurname = nSurname; } // 插入关联的ContactNumber [numbers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){ id obj2 = [labels objectAtIndex:idx]; obj = [obj stringByReplacingOccurrencesOfString:@" " withString:@""]; ContactNumber *phoneNumber = [NSEntityDescription insertNewObjectForEntityForName:@"ContactNumber" inManagedObjectContext:bgContext]; phoneNumber.number = obj; phoneNumber.label = obj2; phoneNumber.nameAndSurname = nameAndSurname; }]; // 先保存后台上下文,把变更推给父上下文 if ([bgContext save:&error]) { // 切换到父上下文的队列保存(父上下文如果是主线程,performBlock会自动在主线程执行) [saveObjectContext performBlock:^{ NSError *parentError; if (![saveObjectContext save:&parentError]) { NSLog(@"父上下文保存失败: %@", parentError.localizedDescription); } }]; } else { NSLog(@"后台上下文保存失败: %@", error.localizedDescription); } }]; }
第二步:让主线程上下文同步变更
有两种方式可以让主线程上下文拿到最新数据:
方式一:查询前手动刷新上下文
在你的查询方法里,先调用refreshAllObjects刷新所有对象:
- (NSArray *)fetchNameAndSurname { AppDelegate *appDel = (AppDelegate *)[UIApplication sharedApplication].delegate; _managedObjectContext = [appDel managedObjectContext]; // 刷新上下文,获取最新数据 [_managedObjectContext refreshAllObjects]; NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"NameAndSurname" inManagedObjectContext:_managedObjectContext]; [fetchRequest setSortDescriptors:@[[[NSSortDescriptor alloc] initWithKey:@"nameSurname" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)]]]; [fetchRequest setEntity:entity]; [fetchRequest setReturnsObjectsAsFaults:NO]; NSError *error = nil; NSArray *fetchedObjects = [_managedObjectContext executeFetchRequest:fetchRequest error:&error]; return [fetchedObjects valueForKey:@"nameSurname"]; }
方式二:监听保存通知自动合并变更(更推荐)
在你的ViewController或者AppDelegate里注册NSManagedObjectContextDidSaveNotification,当任何上下文保存时,自动合并变更到主线程上下文:
// 在viewDidLoad或者AppDelegate的didFinishLaunchingWithOptions里注册通知 - (void)viewDidLoad { [super viewDidLoad]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextDidSave:) name:NSManagedObjectContextDidSaveNotification object:nil]; } // 处理保存通知的方法 - (void)contextDidSave:(NSNotification *)notification { AppDelegate *appDel = (AppDelegate *)[UIApplication sharedApplication].delegate; NSManagedObjectContext *mainContext = [appDel managedObjectContext]; // 确保在主线程执行合并操作 if (![NSThread isMainThread]) { dispatch_async(dispatch_get_main_queue(), ^{ [mainContext mergeChangesFromContextDidSaveNotification:notification]; }); return; } [mainContext mergeChangesFromContextDidSaveNotification:notification]; } // 记得在dealloc里移除监听 - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; }
额外优化点
- 你原来在插入
ContactNumber时,每次循环都调用[bgContext save:&error],这会频繁触发Core Data的持久化操作,非常影响性能,改成最后统一保存一次就好(我上面的修复代码已经调整了)。 - 建议检查
saveObjectContext和你查询用的managedObjectContext是不是同一个实例,如果AppDelegate里维护的是不同的上下文,也要确保它们的持久化存储协调器是同一个。
这样修改后,后台插入的数据就能被主线程查询到啦!
内容的提问来源于stack exchange,提问作者Steven
相关产品推荐
相关产品推荐

