CoreData: перезагрузка UITableView после изменения persistentStore - PullRequest
0 голосов
/ 07 января 2020

У меня есть настройка проекта с CoreData, которая использует NSFetchedResultsController для отображения списка Students. Я использую NSPersistentCloudKitContainer для синхронизации базы данных с CloudKit.

У меня простой экран. При каждом запуске приложения первый экран показывает список всех Students, используя NSFetchedResultsController. У меня есть тысячи Student объектов в моей базе данных, и если я переустановил приложение, NSPersistentCloudKitContainer автоматически начнет извлекать Students из CloudKit, и мой UItableView начнет обновляться с помощью NSFetchedResultsController всего выглядит волшебно, пока приложение внезапно не завершится со следующей ошибкой:

CoreData: fault: серьезная ошибка приложения. Исключение было получено от делегата NSFetchedResultsController во время вызова -controllerDidChangeContent :. Попытка вставить строку 6 в раздел 88, но в разделе 88 после обновления с userInfo (null) есть только 3 строки

Я действительно не мог понять, почему происходит сбой приложения. Именно во время просмотра таблицы пытались обновить записи, но возникли некоторые несоответствия. Я использую стандартный шаблонный код для NSFetchedResultsControllerDelegate. Ниже моя реализация NSFetchedResultsControllerDelegate

extension StudentTableViewController : NSFetchedResultsControllerDelegate{
    public func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        studentTableView.beginUpdates()
    }

    public func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
        switch type {
        case .insert: studentTableView.insertSections([sectionIndex], with: .automatic)
        case .delete: studentTableView.deleteSections([sectionIndex], with: .automatic)
        default: break
        }
    }

    public func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
        switch type {
        case .insert:
            if let newIndexPath = newIndexPath{
                studentTableView.insertRows(at: [newIndexPath], with: .automatic)
            }
        case .delete:
            if let indexPath = indexPath{
                studentTableView.deleteRows(at: [indexPath], with: .automatic)
            }
        case .update:
            if let indexPath = indexPath{
                studentTableView.reloadRows(at: [indexPath], with: .automatic)
            }
        case .move:
            if let indexPath = indexPath{
                studentTableView.deleteRows(at: [indexPath], with: .automatic)
            }
            if let newIndexPath = newIndexPath{
                studentTableView.insertRows(at: [newIndexPath], with: .automatic)
            }
        @unknown default:
            break
        }
    }

    public func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        studentTableView.endUpdates()
    }
}

Альтернативный подход
Поскольку у меня тысячи синхронизируемых объектов, он должен поступать партиями, я также получаю NSPersistentStoreRemoteChange уведомление срабатывает почти 30 раз в течение синхронизации c. TableView обновляется, пока происходит syn c. Я подумал, почему бы не дождаться завершения syn c, а затем просто перезагрузить TableView после завершения syn c, и это не приведет к ненужному обновлению представления таблицы. sh для каждого изменения.

, чтобы отключить автоматическую синхронизацию c, я установил

viewContext.automaticallyMergesChangesFromParent = false
viewContext.setQueryGenerationFrom(.current)

, что делает управляемый текст изолированным от любого изменения фона в постоянном хранилище. Я установил задержку Timer на 5 секунд, и при каждом срабатывании уведомления NSPersistentStoreRemoteChange я сбрасывал Timer. Если в течение 5 секунд не происходит никаких изменений, я пытаюсь синхронизировать c изменения вручную, обновляя queryGeneration до последнего текущего состояния постоянного хранилища.

// Pin the viewContext to the current generation token
    do {
        try container.viewContext.setQueryGenerationFrom(.current)
    } catch {
        fatalError("###\(#function): Failed to pin viewContext to the current generation:\(error)")
    }

Также Apple упомянула в своих документах для разработчиков [Ссылка] :

Refre sh Objects
Refre sh любые управляемые объекты, зарегистрированные в контексте после изменения генерации запроса контекста или открепления контекста. Управляемые объекты автоматически не обновляют sh, так как это поведение может быть нежелательным и его трудно восстановить. Вызовите refreshAllObjects () для контекста, чтобы обновить sh его существующих управляемых объектов.

, вызвав refreshAllObjects () для viewContext, замораживая пользовательский интерфейс на несколько секунд, но он получает правильные данные. Как я могу избежать зависания пользовательского интерфейса во время вызова refreshAllObjects ()?

Есть ли какая-либо ошибка в моем NSFetchedResultsControllerDelegate или есть какой-либо способ refreshAllObjects без зависания пользовательского интерфейса?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...