我最近开始使用
async
/await
(在现有的非 Swift-UI 代码库中)。
我无法得到答案的两件事(既不是通过阅读也不是通过实验)是它是否错误
await
上的东西@MainActor
和
访问任务/其他 Actor 中的属性
根据我的经验,从不同的线程访问属性会导致竞争条件,不应该这样做,并且在主线程上执行阻塞操作会阻塞 UI,不应该这样做。
然而,我看到很多代码就像我在下面的(完全可运行的)示例中试图隔离的代码。
所以在
loadStuff1
中,我们可能会或可能不会在主线程上(取决于谁的电话)。它访问两个属性,stuffLoader
和 param
,它们可能是从主线程读取/写入的。如我所料,这会导致比赛条件吗?你的专业人士做什么呢?
在
loadStuff2
中,我们肯定在主线程上。在这里等待东西安全吗?如果是,那么我想这是要走的路,但我还没有看到任何关于 await not 阻塞主线程的声明。
在
loadStuff3
中,我们启动了一个任务,据我所知,它会将我们的执行转移到另一个线程,该线程永远不会成为主线程。就像loadStuff1
一样,我希望这会产生竞争条件。
import PlaygroundSupport
import UIKit
struct Stuff { }
enum Param {
case a
case b
}
class VM {
private let stuffLoader = StuffLoader()
var param: Param = .a
func loadStuff1() async -> Stuff {
// Is on the Main thread if the calling Thread is the Main Thread.
await stuffLoader.load(param: param)
}
@MainActor func loadStuff2() async -> Stuff {
// Is always on the Main thread.
await stuffLoader.load(param: param)
}
func loadStuff3(completion: @escaping (Stuff) -> Void) {
Task {
// Is never on the Main thread.
let stuff = await stuffLoader.load(param: param)
completion(stuff)
}
}
}
class StuffLoader {
func load(param: Param) async -> Stuff {
// Here, a network call occurs and returns in 1 second.
Stuff()
}
}
class VC: UIViewController {
let vm: VM
init(vm: VM) {
self.vm = vm
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
Task {
let stuff = await vm.loadStuff2()
print("Stuff 2 loaded.")
}
}
func onSomethingHappened() {
Task {
let stuff = await vm.loadStuff1()
print("Stuff 1 loaded.")
}
}
func onSomethingElseHappened() {
vm.loadStuff3 { stuff in
print("Stuff 3 loaded.")
}
}
}
let vm = VM()
PlaygroundPage.current.liveView = VC(vm: vm)