首页 > 解决方案 > 使用 UICollectionView 观察领域 - 竞争条件

问题描述

我将Realm其用作缓存层,这样每当数据呈现给用户时,它首先从数据库中获取并显示给用户。随后,发送服务器请求以获取最新版本的数据,将其与Realm数据库同步并以UICollectionView.

问题是,当从Realm数据库中检索缓存数据并且UICollectionView正在更新时,服务器更新请求有可能在UICollectionView加载所有单元格之前完成,并且由于Results列表是数据的实时集合,它可能有被修改。现在,例如,如果在服务器端删除了一个项目,则实时集合将少持有一个项目,因此会导致超出范围的异常。

话虽如此,考虑到在逐行询问每一行时可以更改Realm这一事实,即使官方文档中提供的代码也不是线程安全的:resultsUITableView

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已经重新加载了所有数据,但它只是被分派到一个队列并准备好异步处理。)

我想听听任何建议如何以有效的方式实现这一点,从而消除上述竞争条件。

标签: iosswiftuicollectionviewrealm

解决方案


您需要在主线程上完成所有 UI 更新。如果你这样做,第一组结果会更新主线程上的集合视图,当下一组结果也出现时,它将在主线程上排队,因此它会在第一组完成后更新。


推荐阅读