在Core Data和NSFetchedResultsController中的iOS中点击我的屏幕之前,重新加载数据不起作用
以下是我目前的情况:在Core Data和NSFetchedResultsController中的iOS中点击我的屏幕之前,重新加载数据不起作用,ios,objective-c,uitableview,core-data,Ios,Objective C,Uitableview,Core Data,以下是我目前的情况: 当我打开应用程序时,我的第一个屏幕上的UITableView为空 我从远程服务器获取数据,解析其生成的JSON对象,并将其保存到核心数据存储中,然后向存储发出获取请求,并将结果显示到前面提到的UITableView 但是,即使在解析保存查询后发布通知并重新加载表之后,表也不会更新,直到我点击或滚动屏幕 我尝试了[[u myTableView reloadData]和[\u myTableView performselectornmainthread:@selector(re
UITableView
为空UITableView
[[u myTableView reloadData]
和[\u myTableView performselectornmainthread:@selector(reloadData)with object:nil waitUntilDone:NO]代码>来自通知,但结果根本没有改变,我仍然需要点击或滚动屏幕才能看到表上重新加载的数据
我使用核心数据和NSFetchedResultsController,并确认我的查询已成功获取。只是重新加载表的问题。还要注意,我从AppDelegate.m
调用通知,但出于上述原因(远程服务器数据获取),我的实际表视图位于另一个类RootViewController.m
另外,当我写出以下代码时:
-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[_myTableView reloadData];
[_myTableView endUpdates];
}
-(void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
NSLog(@"updated begin");
[_myTableView beginUpdates];
}
我仍然需要通过点击屏幕来查看结果。但是,当我添加以下代码时:
-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[_myTableView reloadData];
[_myTableView endUpdates];
}
-(void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
NSLog(@"updated begin");
[_myTableView beginUpdates];
}
我甚至在点击屏幕后也看不到重新加载的数据
此外,使用printf debug,我确认即使多次调用上述两个方法(可能与实际获取的托管对象调用次数相同,对吧?),在获取新添加的数据集后,numberofrowsinssection:
和cellforrowatinexpath:
根本不会被调用
注意,cellforrowsatindepath:
被调用,如果我等待10秒或20秒左右,数据就会显示出来,这意味着即使我没有点击或滚动屏幕,数据也会显示出来。奇怪
那么这里发生了什么?我如何解决这个奇怪的问题
我使用iOS 7和Xcode 5
更新
我发现,当我编写controllerWillChangeContext:
时,无法看到重新加载的数据的原因是我遇到了以下错误:CoreData:error:严重的应用程序错误。在调用-controllerDidChangeContent:期间,从NSFetchedResultsController的委托捕获到异常。无效更新:节0中的行数无效。更新后现有节中包含的行数(1)必须等于更新前该节中包含的行数(0),加上或减去从该节插入或删除的行数(0插入,0删除),加上或减去移入或移出该节的行数(0移入,0移出). 使用userInfo(null)
。当我只实现controllerDidChangeContext:
时,我没有得到这个错误,这仍然很奇怪,但是如果我同时写这两个,我就得到了错误,即使在点击屏幕后,表也不会显示数据
所以也许我不能先显示空白结果,然后用新数据更新表,而应该让用户等待显示正确的数据 在controllerdChangeContent:
中调用reloadData
是错误的方法。实现NSFetchedResultsControllerDelegate时,该委托负责所有表视图更新
从远程服务器的导入应在后台队列中完成。在通过mergeChangesFromContextDidSaveNotification
将背景上下文与主上下文合并之前,您应该
禁用NSFetchedResultsControllerDelegate
将更改与主上下文合并
重新加载您的表
重新启用NSFetchedResultsControllerDelegate
样本:
fetchedResultsController.delegate = nil;
[mainContext mergeChangesFromContextDidSaveNotification:notification];
[tableView reloadData];
fetchedResultsController.delegate = yourController;
提示:使用NSFetchedResultsControllerDelegate时,您必须至少实现:
controllerWillChangeContent:
controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:
controllerDidChangeContent:
阅读示例实现的文档:以下是一些示例代码
在后台加载数据
首先正确实现3个主要的fetchedResultsControllerDelegate方法,以确保UITableView正确获取更新
- controllerWillChangeContent:
- 控制器:didChangeObject:atIndexPath:forChangeType:newIndexPath:
- controllerDidChangeContent:
不要从这些委托方法中调用[tableView reload],仅使用Apple示例代码在tableView中正确添加/删除/更新行
创建背景线程在背景线程上创建managedObjectContext(bgContext)
在bgContext上注册保存通知的观察者(调用方法storesDidSave)
批量运行后台加载/删除作业保存
处理保存通知
后台作业完成后,删除bgContext保存通知的观察者
只要您有权访问主managedObjectContext,就可以将此代码放在任何位置—可能在运行后台加载作业的对象中。确保使用正确的线程进行处理。ManagedObjectContext不是线程安全的
// NB - this may be called from a background thread so make sure we always run on the main thread !!
// This is when transaction logs are loaded
- (void)storesDidSave:(NSNotification*)notification {
// Ignore any notifications from the main thread because we only need to merge data
// loaded from other threads.
if ([NSThread isMainThread]) {
FLOG(@" main thread saved context ignore");
return;
}
// Must pass these to the main thread to process
[[NSOperationQueue mainQueue] addOperationWithBlock:^ {
FLOG(@"storesDidSave ");
// Now merge into the MAIN managedObjectContext and save it.
// If you don't save then objects in the main context don't seem to update correctly,
// especially deletes seem to don't show
if (self.managedObjectContext) {
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
NSError *error;
FLOG(@" saving moc");
if (![self.managedObjectContext save:&error]) {
FLOG(@" error saving context, %@, %@", error, error.userInfo);
}
}
}];
}
// Sample background dispatcher
- (void)loadDataInBackground {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
[self loadData];
});
}
// Background load job calls save after batches of 100 records
- (void)loadData {
FLOG(@"loadData called");
_loadJobCount++;
[self postJobStartedNotification];
FLOG(@" waiting 5 seconds...");
sleep(5);
[self showBackgroundTaskActive];
NSManagedObjectContext *bgContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
// Register for saves in order to merge any data from background threads
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(storesDidSave:) name: NSManagedObjectContextDidSaveNotification object:bgContext];
while (self.persistentStoreCoordinator == nil) {
FLOG(@" persistentStoreCoordinator = nil, waiting 5 seconds to try again...");
sleep(5);
}
bgContext.persistentStoreCoordinator = [self persistentStoreCoordinator];
FLOG(@" starting load...");
for (int i = 1; i<=100; i++) {
// Add a few companies
[self insertNewCompany:bgContext count:10];
[bgContext processPendingChanges];
// Save the context.
NSError *error = nil;
if (![bgContext save:&error]) {
FLOG(@" Unresolved error %@, %@", error, [error userInfo]);
}
FLOG(@" waiting 2 seconds...");
sleep(0.01);
}
// Register for saves in order to merge any data from background threads
[[NSNotificationCenter defaultCenter] removeObserver:self name: NSManagedObjectContextDidSaveNotification object:bgContext];
FLOG(@" loading ended...");
[self showBackgroundTaskInactive];
sleep(2);
_loadJobCount--;
[self postJobDoneNotification];
}
//NB-这可能是从后台线程调用的,因此请确保始终在主线程上运行!!
//此时将加载事务日志
-(无效)存储IDSave:(NSNotification*)通知{
//忽略来自主线程的任何通知,因为我们只需要合并数据
//从其他线程加载。
如果([NSThread isMainThread]){
FLOG(@“主线程已保存co