这是我目前的情况:
当我打开我的应用程序时,第一个屏幕上的
UITableView
是空白的我从远程服务器获取我的数据并解析其结果 JSON 对象,并将其保存到核心数据存储,然后向存储发出获取的请求并将结果显示到上述
UITableView
但是,即使我在 parse-save-query 后发布通知并重新加载我的表格,表格也不会更新,直到我点击或滚动屏幕。
我从通知中尝试了 [_myTableView reloadData];
和 [_myTableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];
,但结果没有根本没有改变,我仍然需要点击或滚动屏幕才能看到表格上重新加载的数据。
我使用 Core Data 和 NSFetchedResultsController,我确认我的查询已成功获取。只是一个重新加载表的问题。还要注意,我从 AppDelegate.m
调用了通知,但由于上述原因(远程服务器数据获取),我的实际 TableView 位于另一个类 RootViewController.m
).
另外,当我写出以下代码时:
-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[_myTableView reloadData];
[_myTableView endUpdates];
}
我仍然需要点击屏幕才能看到结果。但是,当我添加以下代码时:
-(void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
NSLog(@"updated begin");
[_myTableView beginUpdates];
}
即使点击屏幕,我也看不到重新加载的数据。
此外,使用 printf 调试,我确认即使上述两个方法被调用很多次(可能它被调用的次数与实际获取的托管对象的次数相同,对吧?),numberOfRowsInSection :
和 cellForRowAtIndexPath:
在我获取新添加的数据集后根本没有被调用。
请注意,如果我等待 10 或 20 秒左右,就会调用 cellForRowsAtIndexPath:
并显示数据,这意味着即使我没有点击或滚动屏幕也会显示数据。很奇怪。
那么这里发生了什么?我该如何解决这个奇怪的问题?
我使用 iOS 7 和 Xcode 5。
更新
我发现我在编写 controllerWillChangeContext:
时无法看到重新加载的数据的原因是我收到以下错误:CoreData: error: Serious application error。在调用 -controllerDidChangeContent: 期间从 NSFetchedResultsController 的委托(delegate)捕获了一个异常。更新无效:第 0 节中的行数无效。更新 (1) 后现有节中包含的行数必须等于更新 (0) 前该节中包含的行数,加上或减去行数从该部分插入或删除的行数(0 插入,0 删除)加上或减去移入或移出该部分的行数(0 移入,0 移出)。与 userInfo (null)
。当我只实现 controllerDidChangeContext:
时,我没有收到这个错误,这仍然很奇怪,但是如果我同时写这两个,我就会收到错误,即使我点击屏幕。
所以也许我不能采取先显示空白结果,然后用新数据更新表格的方式,而应该让用户等待显示正确的数据...?
最佳答案
这里是一些示例代码
在后台加载数据
首先正确实现 3 个主要的 fetchedResultsControllerDelegate 方法,以确保您的 UITableView 正确获取更新。
- controllerWillChangeContent:
- Controller :didChangeObject:atIndexPath:forChangeType:newIndexPath:
- controllerDidChangeContent:
不要从这些委托(delegate)方法中调用 [tableView reload],仅使用 Apple 示例代码在 tableView 中正确添加/删除/更新行。
- 创建后台线程 在后台线程 (bgContext) 上创建 managedObjectContext
- 注册观察者以在 bgContext 上保存通知(调用方法 storesDidSave)
- 批量运行后台加载/删除作业保存
- 处理保存通知
- 后台作业完成后移除 bgContext 保存通知的观察者
您可以将此代码放在任何地方,只要您有权访问 MAIN 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];
}
关于ios - 在 iOS 中的核心数据和 NSFetchedResultsController 中点击我的屏幕之前,reloadData 无法正常工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21965572/