【发布时间】:2018-07-30 16:32:21
【问题描述】:
我使用Realm 作为缓存层,这样每当数据呈现给用户时,它首先从数据库中获取并显示给用户。随后,发送服务器请求以获取最新版本的数据,将其与Realm 数据库同步并在UICollectionView 中显示更改。
问题在于,当从Realm 数据库检索缓存数据并且UICollectionView 正在更新时,服务器更新请求有可能在UICollectionView 加载所有单元格之前完成,并且由于Results 列表是数据的实时集合,它可以被修改。现在,例如,如果在服务器端删除了一个项目,则实时集合将少持有一个项目,因此会导致超出范围的异常。
话虽如此,即使Realm 官方文档中提供的代码也不是线程安全的,考虑到results 可以更改而UITableView 逐行请求每一行:
class ViewController: UITableViewController {
var notificationToken: NotificationToken? = nil
override func viewDidLoad() {
super.viewDidLoad()
let realm = try! Realm()
let results = realm.objects(Person.self).filter("age > 5")
// Observe Results Notifications
notificationToken = results.observe { [weak self] (changes: RealmCollectionChange) in
guard let tableView = self?.tableView else { return }
switch changes {
case .initial:
// Results are now populated and can be accessed without blocking the UI
tableView.reloadData()
case .update(_, let deletions, let insertions, let modifications):
// Query results have changed, so apply them to the UITableView
tableView.beginUpdates()
tableView.insertRows(at: insertions.map({ IndexPath(row: $0, section: 0) }),
with: .automatic)
tableView.deleteRows(at: deletions.map({ IndexPath(row: $0, section: 0)}),
with: .automatic)
tableView.reloadRows(at: modifications.map({ IndexPath(row: $0, section: 0) }),
with: .automatic)
tableView.endUpdates()
case .error(let error):
// An error occurred while opening the Realm file on the background worker thread
fatalError("\(error)")
}
}
}
deinit {
notificationToken?.invalidate()
}
}
我能想到解决此问题的唯一方法是创建结果的深层副本,并使用 Semaphore 或类似方法同步观察函数的主体,以确保数据不会处于不一致状态,我考虑非常低效。 (注意tableView.endUpdates() 并不意味着UITableView 已经重新加载了所有数据,但它只是被分派到队列并准备异步处理。)
我想听听任何建议如何以有效的方式实现这一点,从而消除上述竞争条件。
【问题讨论】:
标签: ios swift uicollectionview realm