英文:
How can I make this code concurrency safe?
问题
我有以下用Swift编写的代码:
```swift
protocol Theme {
var iconColor: Color { get }
}
struct MyTheme: Theme {
var iconColor: Color = .white
}
class Appearance {
static var theme: Theme = MyTheme()
}
我可以在我的视图中使用这个Appearance
类来方便地获取当前主题的属性:
Image("my-icon")
.foregroundColor(Appearance.theme.iconColor)
我在XCode中启用了-warn-concurrency
编译标志,作为一个实验,看看我的应用程序会产生多少警告。这是其中一个警告:
Reference to static property 'theme' is not concurrency-safe because it involves shared mutable state
现在我明白为什么会出现这个警告,但我该如何更改代码,以使theme
在并发环境中是安全的,同时仍然像现在这样方便?
我可以将theme
更改为static let
,但那样我将无法在运行时更改应用程序的主题。我还尝试了@MainActor
和actor Appearance
的组合,但它们要么产生相同的警告,要么产生其他错误。
编辑
@MainActor class Appearance
可以消除所有警告,但请考虑以下情况:
struct FooView: View {
var iconColor: Color = Appearance.theme.iconColor
var body: some View {
HStack {
Text("FooView")
Image("foo-logo")
.foregroundColor(iconColor)
}
}
}
上面的代码会产生错误:Main actor-isolated static property 'theme' cannot be referenced from a non-isolated context
<details>
<summary>英文:</summary>
I have the following code written in Swift:
protocol Theme {
var iconColor: Color { get }
}
struct MyTheme: Theme {
var iconColor: Color = .white
}
class Appearance {
static var theme: Theme = MyTheme()
}
I can use this `Appearance` class in my views to conveniently get the current theme's properties
Image("my-icon")
.foregroundColor(Appearance.theme.iconColor)
I enabled `-warn-concurrency` compiler flag in XCode as an experiment to see how many warnings my app produces. This is one of the warnings:
`Reference to static property 'theme' is not concurrency-safe because it involves shared mutable state`
Now I understand why it gives this warning, but how could I change the code so that `theme` is concurrency safe and still as convenient as it is?
I could change `theme` to `static let` but then I would not be able to change the theme of the app during runtime. I also tried combinations of `@MainActor` and `actor Appearance` but they give mixed result with either the same warning or some other error.
**Edit**
`@MainActor class Appearance` gets rid of all the warnings, but consider this:
struct FooView: View {
var iconColor: Color = Appearance.theme.iconColor
var body: some View {
HStack {
Text("FooView")
Image("foo-logo")
.foregroundColor(iconColor)
}
}
}
The code above will give an error: `Main actor-isolated static property 'theme' can not be referenced from a non-isolated context`
</details>
# 答案1
**得分**: 2
要消除警告,您只需将`@MainActor`添加到`Appearance`类中。如果您在`body`之外使用`FooView`,还需要将其标记为`@MainActor`,因为`body`是`MainActor`隔离的,但`FooView`不是。
话虽如此,这在实践中不会起作用。SwiftUI 不会知道`Appearance.theme`已更改并相应地更新您的视图颜色。在 SwiftUI 中实现这样的“主题”系统的方式是使用`EnvironmentObject`。
```swift
class Appearance: ObservableObject {
@Published var theme: Theme = MyTheme()
}
struct YourRootView: View {
// 在每个需要访问主题的视图中编写此代码
@EnvironmentObject var appearance: Appearance
var body: some View {
Text("Foo")
.foregroundColor(appearance.theme.iconColor)
}
}
当您创建YourRootView
时,请为其提供一个新的Appearance
对象。
@main
struct FooApp: App {
var body: some Scene {
WindowGroup {
YourRootView()
.environmentObject(Appearance())
}
}
}
现在 SwiftUI 可以观察theme
属性的更改。
英文:
To remove the warning, you can just add @MainActor
to the Appearance
class. You would also need to mark FooView
with @MainActor
if you are using it outside of body
, because body
is MainActor
-isolated, but FooView
is not.
That said, this won't work in practice. SwiftUI won't know that Appearance.theme
has changed and update your views' colors accordingly. The SwitUI way of implementing such a "theme" system is to use an EnvironmentObject
.
class Appearance: ObservableObject {
@Published var theme: Theme = MyTheme()
}
struct YourRootView: View {
// write this in every view that needs to access the theme
@EnvironmentObject var appearance: Appearance
var body: some View {
Text("Foo")
.foregroundColor(appearance.theme.iconColor)
}
}
When you create YourRootView
, give it a new Appearance
object.
@main
struct FooApp: App {
var body: some Scene {
WindowGroup {
YourRootView()
.environmentObject(Appearance())
}
}
}
Now SwiftUI can observe the changes to the theme
property.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论