ios - 在 iOS 中的核心数据和 NSFetchedResultsController 中点击我的屏幕之前,reloadData 无法正常工作

标签 ios objective-c uitableview core-data

这是我目前的情况:

  1. 当我打开我的应用程序时,第一个屏幕上的 UITableView 是空白的

  2. 我从远程服务器获取我的数据并解析其结果 JSON 对象,并将其保存到核心数据存储,然后向存储发出获取的请求并将结果显示到上述 UITableView

  3. 但是,即使我在 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 中正确添加/删除/更新行。

  1. 创建后台线程 在后台线程 (bgContext) 上创建 managedObjectContext
  2. 注册观察者以在 bgContext 上保存通知(调用方法 storesDidSave)
  3. 批量运行后台加载/删除作业保存
  4. 处理保存通知
  5. 后台作业完成后移除 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/

相关文章:

ios - 是否可以根据 map View 的中心抓取城市等信息

ios - 在 .m 文件的@interface 中初始化的 CGPoint 变量不能有值

ios - CLLocationManagerdesiredAccuracy of kCLLocationAccuracyHundredMeters 导致位置更新非常不频繁

ios - 如何为 cell.imageView.frame 设置框架?

ios - 如何在 Swift 3 的 CGFloat 扩展中使用可变变量

iphone - PDF iOS 应用程序的离线存储

ios - HealthKit HKAuthorizationStatus 读取数据

iOS 7 本地通知默认没有声音

ios - 以编程方式将两个 UITableView 堆叠在一个 View 中?

swift - uitableViewCell 错误的背景颜色