swift - 主线程和 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()
完成之前不会改变吗?
解决方案
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
所以,总而言之,如果这就是你现在正在做的事情,那么这是正确的做法。
推荐阅读
- modelica - 具有空时间常数的一阶 Modelica 模型
- jenkins - Jenkins 文件,检查 Globals 变量是否存在
- r - 使用 r 中的 fable 包为 ARIMA “‘滞后’或‘差异’错误的坏值”寻求帮助
- pyspark - 在 pyspark 中随时间窗口删除重复项
- python - 产生随机结果的麻烦
- c++ - 将 Crashpad 与 MacOS Qt 应用程序集成
- javascript - 使用 x 键查找每个对象值并更改值的类型
- python - Python:在数组中查找最大元素X的子数组,其总和小于或等于给定数字
- sparql - SPARQL 上的超级简单查询什么也不检索
- .net - 检索现有 Azure 表项时 TableResult 挂起