У меня есть приложение с табличным представлением.При запуске приложение импортирует более 100 000 строк json в CoreData.
Я использую пакеты (250 элементов) и NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
для записи в базу данных по частям, а не для блокировки основного потока.
Эти импортированные данные должны отображаться в табличном представлении, и пользователь сможетпрокручивать представление таблицы плавно во время импорта.
Ранее я пытался использовать .mainQueueConcurrencyType
для контекста для NSFetchedResultsController, но он останавливает прокрутку во время слияния из других частных контекстов. (, поскольку слияние выполняется в главном потоке ).
Я создал NSFetchedResultsController
для отображения данных в табличном представлении и установил NSManagedObjectContext с concurrencyType = .privateQueueConcurrencyType.
ИтакNSFetchedResultsController
вызывает func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>)
и func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>)
в фоновом потоке и изменяет свойство fetchedObjects
также в фоновом потоке .
НО func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
для вызовов табличного представления из основного потока .
Таким образом, fetchedObjects
может быть изменено в любое время ( фоновым потоком ) вне зависимости отосновной поток, поэтому он производит сбой .
(Например, когда fetchedObjects
были удалены и теперь содержат 0 элементов, но func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
может иметь предыдущее значение fetchedObjects.count
.)
Вопрос: Каксинхронизировать фоновый поток контекста CoreData и доступ к данным из основного потока TableView?
Код:
class QuakesViewController: UITableViewController {
var tmpQuakes = [Quake]()
var changesStarted: Bool = false {
didSet {
tmpQuakes = changesStarted ? dataProvider.fetchedResultsController.fetchedObjects ?? [] : []
}
}
func getQuake(byIndexPath indexPath: IndexPath) -> Quake? {
return changesStarted ? tmpQuakes[indexPath.row] : dataProvider.fetchedResultsController.fetchedObjects?[indexPath.row]
}
func getCount() -> Int {
return changesStarted ? tmpQuakes.count : dataProvider.fetchedResultsController.fetchedObjects?.count ?? 0
}
}
// MARK: - UITableViewDataSource
extension QuakesViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "QuakeCell", for: indexPath) as? QuakeCell else {
print("Error: tableView.dequeueReusableCell doesn'return a QuakeCell!")
return QuakeCell()
}
guard let quake = getQuake(byIndexPath: indexPath) else { return cell }
cell.configure(with: quake)
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return getCount()
}
}
// MARK: - NSFetchedResultsControllerDelegate
extension QuakesViewController: NSFetchedResultsControllerDelegate {
/**
Reloads the table view when the fetched result controller's content changes.
*/
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
DispatchQueue.main.sync {
self.changesStarted = false
self.tableView.reloadData()
}
}
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
DispatchQueue.main.sync {
self.changesStarted = true
}
}
}
Можно ли выполнить синхронизацию без дополнительных переменных (например, tmpQuakes
и changesStarted
)?
Та же проблема, что и: Дочерние контексты CoreData, NSFetchedResultsController и основной поток