首页 > 解决方案 > 主线程和 Collectionview.reloadData()

问题描述

我有一种情况,代码更改了它的 collectionView 数据源然后调用reloadData()了几次,我认为这会导致我的应用程序由于竞争条件而崩溃,因为数据源变化非常快,所以我包装了代码片段(更改数据源然后重新加载集合视图) DispatchQueue.main.async并且崩溃消失了。所以这个问题是否有意义并解决了竞争条件,我的理解是包装这段代码DispatchQueue.main.async在 reloadData() 函数完成之前不会再次执行这段代码,对吗?如果您对如何解决此问题有任何其他想法,那就太好了

更新:

例如,如果我们在主线程上有这段代码

self.collectionView.dataSource = self.dataSource
self.collectionView.reloadData()

并且这段代码一个接一个地快速调用了两次,因此数据源在第一次重新加载时发生了变化,作为一种解决方案,我用异步块包装了这段代码,以确保数据源在第一次重新加载完成之前不会改变。如下所示:

DispatchQueue.main.async {
       self.collectionView.dataSource = self.dataSource
       self.collectionView.reloadData()
}

这会保证数据源在 reloadData()完成之前不会改变吗?

标签: swiftuicollectionviewreloaddatadispatch-queue

解决方案


reloadData始终在主线程上调用。如果你不这样做,就是崩溃。

好的,但是现在让我们来谈谈数据的变化和调用reloadData是如何耦合的。也许你有这样的情况:

background queue:
=================
    <networking>
    update the data
    main queue:
    ===========
        reloadData

您仍然可能会遇到麻烦,因为尽管对主队列的两次调用不能重叠,但对后台队列的两次调用可能重叠,因为它可能是一个并发队列。所以这就是你的比赛条件;数据的更新和数据的重新加载是分离的。请记住,在重新加载数据期间调用了多个数据源方法,因此它们都需要查看相同的数据,否则如果数据在调用之间发生更改,您确实会因某种不一致问题而崩溃。有了这种架构,这是一个非常现实的可能性。

但是如果后台队列是一个串行队列,并且如果到主队列的步骤是从那个串行队列开始的,那么这种重叠,至少是不可能的。所以这将是一个更安全的解决方案:

background queue:
=================
    <networking>
    single background *serial* queue:
    =================================
        update the data
        main queue:
        ===========
            reloadData

尽管如此,我们仍然处于“共享数据”的状态。危险依然存在。没有什么可以阻止数据同时在其他队列上被触及。为防止这种情况发生,您需要围绕对数据的每次访问编写代码,确保访问仅发生在一个队列上。毕竟,您的数据结构本身可能不是线程安全的!但是我们已经知道数据源方法将只能在主线程上查看数据,因为这就是reloadData工作原理。所以最简单的方法就是制定一个规则,每次对数据的访问都发生在主队列上:

background queue:
=================
    <networking>
    main queue:
    ===========
        update the data
        reloadData

所以,总而言之,如果这就是你现在正在做的事情,那么这是正确的做法。


推荐阅读