协议扩展中不支持异步/等待



在我的iOS应用程序中,我有一个类似于以下的协议扩展:

protocol ViewControllerBase: UIViewController {
}
extension ViewControllerBase {
func showItem(itemID: Int) {
Task {
await loadItemDetails(itemID)
let itemDetailsViewController = ItemDetailsViewController(style: .grouped)
present(itemDetailsViewController, animated: true)
}
}
func loadItemDetails(_ itemID: Int) async {
}
}

我正在使用一个协议扩展,以便所有实现ViewControllerBase的视图控制器都将继承showItem()方法。ItemDetailsViewController定义如下:

class ItemDetailsViewController: UITableViewController {
}

loadItemDetails()的调用编译良好,但对ItemDetailsViewController初始值设定项和present()方法的调用会产生以下编译器错误:

表达式为"async",但未标记为"await">

这对我来说没有意义,因为初始化器和方法不是异步的。此外,我在其他地方使用相同的模式,没有问题。例如,如果我将ViewControllerBase转换为一个类,它编译得很好:

class ViewControllerBase: UIViewController {
func showItem(itemID: Int) {
Task {
await loadItemDetails(itemID)
let itemDetailsViewController = ItemDetailsViewController(style: .grouped)
present(itemDetailsViewController, animated: true)
}
}
func loadItemDetails(_ itemID: Int) async {
}
}

它似乎与UIKit特别相关,因为这段代码也编译得很好:

class ItemDetails {
}
protocol Base {
}
extension Base {
func showItem(itemID: Int) {
Task {
await loadItemDetails(itemID)
let itemDetails = ItemDetails()
present(itemDetails)
}
}
func loadItemDetails(_ itemID: Int) async {
}
func present(_ itemDetails: ItemDetails) {
}
}

是否有原因不允许在协议扩展中使用此代码,或者这可能是Swift编译器中的错误?

这对我来说没有意义,因为初始化器和方法不是异步的。

是的,但在另一种情况下,您必须说await,并且目标方法被视为async:即,当存在跨参与者上下文切换时。

这就是我的意思。

与ItemDetails/Base示例相比,UIKit的特殊之处在于,UIKit接口对象/方法被标记为@MainActor,要求在主要参与者(实际上是指主线程(上运行。

但是在您的协议扩展代码中,没有任何东西需要在任何特定的参与者上运行。因此,当您调用ItemDetailsViewController初始值设定项或present方法时,编译器会对自己说:

"嗯,当这个代码(showItem(运行时,我们可能不是主要参与者。因此,这些对ItemDetailsViewController方法的调用可能需要上下文开关[从一个参与者更改为另一个参与者]。(">

当您需要将被调用的方法视为async并称为await时,上下文切换正是;因此编译器强制执行该要求。

使该要求在这里消失的简单方法是将您的方法也标记为@MainActor。这样,编译器就知道当运行以下代码时,将不会出现上下文切换:

extension ViewControllerBase {
@MainActor func showItem(itemID: Int) {
Task {
await loadItemDetails(itemID)
let itemDetailsViewController = ItemDetailsViewController(style: .grouped)
present(itemDetailsViewController, animated: true)
}
}
func loadItemDetails(_ itemID: Int) async {
}
}

最新更新