我有以下在内部使用 async/await 的结构 (
View
)。
struct CachedAsyncImage<I: View, P: View>: View {
@State var image: UIImage?
let content: (Image) -> I
let placeholder: () -> P
let url: URL?
public init(
url: URL?,
@ViewBuilder content: @escaping (Image) -> I,
@ViewBuilder placeholder: @escaping () -> P
) {
self.url = url
self.content = content
self.placeholder = placeholder
}
var body: some View {
async {
guard let url = self.url else {
return
}
let session = URLSession.shared
let taskResponse = try? await session.data(from: url, delegate: nil)
guard let imageData = taskResponse?.0,
let uiImage = UIImage(data: imageData) else {
return
}
image = uiImage
}
guard let image = self.image else {
return AnyView(placeholder())
}
return AnyView(content(Image(uiImage: image)))
}
}
我使内部视图(
I
和P
类型)进行相应交换的唯一方法是从异步块内部分配image
属性,该属性必须用@State
属性包装器进行标记。从线程/线程安全的角度来看,这是一种安全的做法还是我必须以其他方式实现它?
是的(但应该在
.task
或 .task(id:)
修饰符中完成,而不是直接在主体本身中完成)。
您可以安全地从任何线程改变状态属性。
https://developer.apple.com/documentation/swiftui/state
您应该尝试在 Swift 中使用结构体等值类型而不是对象的原因之一。
此答案不再相关,请查看 Malhal 的帖子以获取最新答案。
从主线程以外的另一个线程更新 UI 数据是不安全的。并且 Swift 的并发模型无法保证异步代码运行的线程。
所以你的问题的答案是否定的。
您需要在主线程上调度属性更新。而主角是推荐的方式来做到这一点。
只需为此添加一个新功能
@MainActor func updateImage(to newImage: UIImage) {
image = newImage
}
,并从异步任务中调用它:
await updateImage(to: uiImage)