Swift 5.5:异步 @objc didPullToRefresh 选择器导致应用程序崩溃,并出现错误 EXC_BAD_ACCESS

cvl*_*vld 2 ios async-await uirefreshcontrol swift5

我有一个表,其中添加了刷新控件,当我下拉刷新内容时,我重置为表提供数据的数组,然后立即通过 API 调用请求新数据。

到目前为止,我已经使用完成处理程序和协议将数据获取到表视图中,但由于网络调用和嵌套闭包金字塔所需的复杂性,我想将逻辑移至 async/await。

在 viewDidLoad 中填充视图工作正常,但使用 pullToRefresh 选择器时出现错误:

Thread 1: EXC_BAD_ACCESS (code=1, address=0xbcf917df8160)
Run Code Online (Sandbox Code Playgroud)

执行:

override func viewDidLoad() {
    super.viewDidLoad()
    setupView()
    setupTableView()
    setupTableRefreshControl()
    Task {
      await getBalances() //async network call
      myTable.reloadData()
    }
  } 
Run Code Online (Sandbox Code Playgroud)
func setupTableRefreshControl() {
    myTable.refreshControl = UIRefreshControl()
    myTable.refreshControl?.addTarget(self, action: #selector(didPullToRefresh), for: .valueChanged)
  }
Run Code Online (Sandbox Code Playgroud)

导致应用程序崩溃的代码:

   @objc func didPullToRefresh() async {
    balance.reset() // reset array to []
    Task {
      await getBalances() //async network call
      myTable.reloadData()
    }
  }
Run Code Online (Sandbox Code Playgroud)

ACL*_*ima 5

在撰写本文时,@objc选择器和async方法不能很好地协同工作,可能会导致运行时崩溃,而不是编译时错误。

下面是一个示例,说明在将代码转换为 async/await 时,无意中复制此问题是多么容易:我们将以下方法标记为async

@objc func myFunction() async {
    //...
Run Code Online (Sandbox Code Playgroud)

没有注意到它也被标记为@objc并用作选择器

NotificationCenter.default.addObserver(
    self,
    selector: #selector(myFunction),
    name: "myNotification",
    object: nil
)
Run Code Online (Sandbox Code Playgroud)

而在其他地方,则会发布通知

NotificationCenter.default.post(name: "myNotification", object: nil)
Run Code Online (Sandbox Code Playgroud)

繁荣 EXC_BAD_ACCESS

相反,我们应该为我们全新的异步方法提供一个包装选择器

@objc
func myFunctionSelector() {
    Task {
        await myFunction()
    }
}

func myFunction() async { 
    //... 
Run Code Online (Sandbox Code Playgroud)

并将其用于选择器

NotificationCenter.default.addObserver(
    self,
    selector: #selector(myFunctionSelector),
    name: "myNotification",
    object: nil
)
Run Code Online (Sandbox Code Playgroud)