我有一个最初为空的表视图。一旦一个异步方法被称为Networking().fetchRecipies(ingredients: searchText)
,我就尝试更新它。但是,在调用此方法之后,recipieTableView
仍处于未填充状态。是什么导致了这个错误?下面是我正在运行的代码,每次文本字段中的文本更改时都应该运行该代码。我知道这是有效的,因为我也打印API调用的结果
@IBOutlet weak var recipieTableView: UITableView!
var recipies = LocalData.recipies.recipieList
var filteredRecipies = [Recipie]()
override func viewDidLoad() {
filteredRecipies = recipies
super.viewDidLoad()
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
Networking().fetchRecipies(ingredients: searchText)
self.recipieTableView.reloadData()
DispatchQueue.main.async {
self.recipieTableView.reloadData()
self.recipieTableView.beginUpdates()
self.recipieTableView.endUpdates()
}
}
但是,在调用此方法之后,recipieTableView仍处于未填充状态。出现此错误的原因
导致错误的原因是,当您说";在";你实际上应该说";之前"!您的代码没有按编写顺序运行。它按以下顺序运行:
Networking().fetchRecipies(ingredients: searchText) // 2
self.recipieTableView.reloadData() // 1...
// ...
换句话说,所有对reloadData
的调用都发生在获取配方之前。
您需要编写fetchRecipes
,以便它在获取完成后重新加载表视图。
您说方法Networking().fetchRecipies(ingredients:)
是异步调用的,这基本上意味着您的代码进入另一个线程,同时继续执行到异步函数调用方法的末尾。
因此,当您的数据尚未填充时,会调用self.recipieTableView.reloadData()
行。
之后,您可以调用DispatchQueue.main.async
,这也是不必要的。由于您已经在主线程上了(因为这是searchBar(_:textDidChange:)
的约定,就像大多数UIKit方法一样(,您的方法所做的唯一事情就是在一段时间后运行调度的代码块的内容(但在获取请求完成之前,可能会更早(。
在官方指南中阅读更多关于iOS上并发工作原理的信息。如果您是初学者,了解串行队列和并发队列以及sync
和async
调度之间的区别就足够了。
回到你的问题上来。我建议您检查Networking
类的API,并检查是否有任何方法可以在实际提取数据时通知您。大多数iOS API都提供了一个完成块,该块使用作为参数传递的新获取的数据来执行。例如,它可能看起来如下:
Networking().fetchRecipies(ingredients: searchText, completion: { fetchedIngredients in
// ... update your data source array
// reload table view
})
如果在主队列上调用tableView.updateData()
,则直接从该块内部调用它;如果从后台队列调用完成块,则将其包装在DispatchQueue.main.async { }
中(请参阅API了解(。
而且,最重要的是,在调用updateData()
之前,不要忘记更新作为表数据源的数组(我相信它是recipies
或filteredRecipies
(。