Async/await 协议扩展不支持?
Async/await not supported in protocol extension?
在我的 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()
方法的调用产生以下编译器错误:
Expression is 'async' but is not marked with 'await'
这对我来说没有意义,因为初始化程序和方法不是异步的。此外,我在其他地方使用相同的模式没有问题。例如,如果我将 ViewControllerBase
转换为 class,它编译得很好:
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 编译器中的错误?
This doesn't make sense to me, since the initializer and method are not asynchronous.
是的,但是还有一种情况是你必须说await
而目标方法被视为async
:即当有一个cross-actor上下文切换时。
这就是我的意思。
与您的 ItemDetails / Base 示例相反,UIKit 的特别之处在于 UIKit 接口对象/方法被标记为 @MainActor
,要求主要参与者 运行(意思是,在效果,主线程)。
但是在您的协议扩展代码中,没有任何东西需要 运行 任何特定的参与者。因此,当您调用 ItemDetailsViewController 初始化程序或 present
方法时,编译器会对自己说:
“嗯,当这段代码 (showItem
) 运行 时,我们可能不是主角。所以这些对 ItemDetailsViewController 方法的调用可能需要 上下文切换 [从一个演员变成另一个演员].)"
当您需要将调用的方法视为async
并说await
时,上下文切换是恰好;因此编译器会强制执行该要求。
消除该要求的简单方法是将您的 方法也标记为@MainActor
。这样,编译器知道当此代码 运行s:
时会有 no 上下文切换
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 {
}
}
在我的 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()
方法的调用产生以下编译器错误:
Expression is 'async' but is not marked with 'await'
这对我来说没有意义,因为初始化程序和方法不是异步的。此外,我在其他地方使用相同的模式没有问题。例如,如果我将 ViewControllerBase
转换为 class,它编译得很好:
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 编译器中的错误?
This doesn't make sense to me, since the initializer and method are not asynchronous.
是的,但是还有一种情况是你必须说await
而目标方法被视为async
:即当有一个cross-actor上下文切换时。
这就是我的意思。
与您的 ItemDetails / Base 示例相反,UIKit 的特别之处在于 UIKit 接口对象/方法被标记为 @MainActor
,要求主要参与者 运行(意思是,在效果,主线程)。
但是在您的协议扩展代码中,没有任何东西需要 运行 任何特定的参与者。因此,当您调用 ItemDetailsViewController 初始化程序或 present
方法时,编译器会对自己说:
“嗯,当这段代码 (showItem
) 运行 时,我们可能不是主角。所以这些对 ItemDetailsViewController 方法的调用可能需要 上下文切换 [从一个演员变成另一个演员].)"
当您需要将调用的方法视为async
并说await
时,上下文切换是恰好;因此编译器会强制执行该要求。
消除该要求的简单方法是将您的 方法也标记为@MainActor
。这样,编译器知道当此代码 运行s:
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 {
}
}