在并发执行的代码中使用对捕获变量的引用
Use reference to captured variable in concurrently-executing code
更新:这个问题得到了很多意见。如果您认为问题可以通过您自己遇到错误的情况得到加强,请在评论中简要描述您的情况,以便我们使本次问答更有价值。如果您对问题的版本有解决方案,请将其添加为答案。
更新 2:我怀疑这个问题因为我描述的可能解决方案而被投票。为了清楚起见,将其突出显示。
我想在使用 Task.detached
和 async
函数完成异步后台工作后更新 UI。
但是,我在构建过程中遇到构建错误 Reference to captured var 'a' in concurrently-executing code
错误。
我尝试了一些方法,在更新 UI 之前将变量转换为 let constant
是唯一可行的方法。为什么我需要在能够更新 UI 之前创建一个 let 常量?有其他选择吗?
class ViewModel: ObservableObject {
@Published var something: String?
init() {
Task.detached(priority: .userInitiated) {
await self.doVariousStuff()
}
}
private func doVariousStuff() async {
var a = "a"
let b = await doSomeAsyncStuff()
a.append(b)
something = a /* Not working,
Gives
- runtime warning `Publishing changes from background threads
is not allowed; make sure to publish values from the main
thread (via operators like receive(on:)) on model updates.`
or, if `something` is @MainActor:
- buildtime error `Property 'something' isolated to global
actor 'MainActor' can not be mutated from this context`
*/
await MainActor.run {
something = a
} /* Not working,
Gives buildtime error "Reference to captured var 'a' in
concurrently-executing code" error during build
*/
DispatchQueue.main.async {
self.something = a
} /* Not working,
Gives buildtime error "Reference to captured var 'a' in
concurrently-executing code" error during build
*/
/*
This however, works!
*/
let c = a
await MainActor.run {
something = c
}
}
private func doSomeAsyncStuff() async -> String {
return "b"
}
}
让你的可观察对象成为主要演员,比如
@MainActor // << here !!
class ViewModel: ObservableObject {
@Published var something: String?
init() {
Task.detached(priority: .userInitiated) {
await self.doVariousStuff()
}
}
private func doVariousStuff() async {
var a = "a"
let b = await doSomeAsyncStuff()
a.append(b)
something = a // << now this works !!
}
private func doSomeAsyncStuff() async -> String {
return "b"
}
}
测试 Xcode 13 / iOS 15
您可以使用 @State
和 .task
如下:
struct ContentView: View {
@State var result = ""
var body: some View {
HStack {
Text(result)
}
.task {
result = await Something.doSomeAsyncStuff()
}
}
}
任务在视图出现时开始,在视图消失时取消。此外,如果您使用 .task(id:)
,它将在 id 值更改时重新启动(也取消之前的任务)。
异步函数可以放在几个不同的地方,通常是某个地方,这样它就可以独立测试。
struct Something {
static func doSomeAsyncStuff() async -> String {
return "b"
}
}
更新:这个问题得到了很多意见。如果您认为问题可以通过您自己遇到错误的情况得到加强,请在评论中简要描述您的情况,以便我们使本次问答更有价值。如果您对问题的版本有解决方案,请将其添加为答案。
更新 2:我怀疑这个问题因为我描述的可能解决方案而被投票。为了清楚起见,将其突出显示。
我想在使用 Task.detached
和 async
函数完成异步后台工作后更新 UI。
但是,我在构建过程中遇到构建错误 Reference to captured var 'a' in concurrently-executing code
错误。
我尝试了一些方法,在更新 UI 之前将变量转换为 let constant
是唯一可行的方法。为什么我需要在能够更新 UI 之前创建一个 let 常量?有其他选择吗?
class ViewModel: ObservableObject {
@Published var something: String?
init() {
Task.detached(priority: .userInitiated) {
await self.doVariousStuff()
}
}
private func doVariousStuff() async {
var a = "a"
let b = await doSomeAsyncStuff()
a.append(b)
something = a /* Not working,
Gives
- runtime warning `Publishing changes from background threads
is not allowed; make sure to publish values from the main
thread (via operators like receive(on:)) on model updates.`
or, if `something` is @MainActor:
- buildtime error `Property 'something' isolated to global
actor 'MainActor' can not be mutated from this context`
*/
await MainActor.run {
something = a
} /* Not working,
Gives buildtime error "Reference to captured var 'a' in
concurrently-executing code" error during build
*/
DispatchQueue.main.async {
self.something = a
} /* Not working,
Gives buildtime error "Reference to captured var 'a' in
concurrently-executing code" error during build
*/
/*
This however, works!
*/
let c = a
await MainActor.run {
something = c
}
}
private func doSomeAsyncStuff() async -> String {
return "b"
}
}
让你的可观察对象成为主要演员,比如
@MainActor // << here !!
class ViewModel: ObservableObject {
@Published var something: String?
init() {
Task.detached(priority: .userInitiated) {
await self.doVariousStuff()
}
}
private func doVariousStuff() async {
var a = "a"
let b = await doSomeAsyncStuff()
a.append(b)
something = a // << now this works !!
}
private func doSomeAsyncStuff() async -> String {
return "b"
}
}
测试 Xcode 13 / iOS 15
您可以使用 @State
和 .task
如下:
struct ContentView: View {
@State var result = ""
var body: some View {
HStack {
Text(result)
}
.task {
result = await Something.doSomeAsyncStuff()
}
}
}
任务在视图出现时开始,在视图消失时取消。此外,如果您使用 .task(id:)
,它将在 id 值更改时重新启动(也取消之前的任务)。
异步函数可以放在几个不同的地方,通常是某个地方,这样它就可以独立测试。
struct Something {
static func doSomeAsyncStuff() async -> String {
return "b"
}
}