swift - 如何使用@MainActor 初始化全局变量?
问题描述
我想要某种使用同步的全局变量@MainActor
。
这是一个示例结构:
@MainActor
struct Foo {}
我想要一个像这样的全局变量:
let foo = Foo()
但是,这不会编译并与Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
.
很公平。我试图在主线程上构建它,如下所示:
let foo = DispatchQueue.main.sync {
Foo()
}
这编译!但是,它会崩溃EXC_BAD_INSTRUCTION
,因为DispatchQueue.main.sync
无法在主线程上运行。
我还尝试创建一个包装函数,例如:
func syncMain<T>(_ closure: () -> T) -> T {
if Thread.isMainThread {
return closure()
} else {
return DispatchQueue.main.sync(execute: closure)
}
}
并使用
let foo = syncMain {
Foo()
}
但是编译器无法识别if Thread.isMainThread
并再次抛出相同的错误消息,Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
.
这样做的正确方法是什么?我需要某种可以在应用程序启动之前初始化的全局变量。
解决方案
一种方法是将变量存储在容器中(就像enum
充当抽象命名空间一样),并将其与主要参与者隔离。
@MainActor
enum Globals {
static let foo = Foo()
}
同样有效的方法是在对象本身上具有“类似单例”的static
属性,它具有相同的目的,但没有附加对象。
@MainActor
struct Foo {
static let global = Foo()
}
您现在可以通过 访问全局对象Foo.global
。
需要注意的一点是,这将被延迟初始化(在第一次调用时),而不是立即初始化。但是,您可以通过对对象进行任何访问来尽早强制初始化。
// somewhere early on
_ = Foo.global
Swift 5.5 中的错误(已在 Swift 5.6 中解决)
TL;DR:@MainActor 不会在主线程上调用某些东西。这对您来说可能是也可能不是问题。
虽然这可以编译并工作,但似乎这可能会从主线程调用初始化程序,随后在初始化程序内部进行任何调用。
@MainActor
struct Bar {
init() {
print("bar init is main", Thread.isMainThread)
}
func barCall() {
print("bar call is main", Thread.isMainThread)
}
}
@MainActor
struct Foo {
@MainActor
static let global = Foo()
init() {
print("foo init is main", Thread.isMainThread)
let b = Bar()
b.barCall()
}
func fooCall() {
print("foo call is main", Thread.isMainThread)
}
}
Task.detached {
await Foo.global.fooCall()
}
// prints:
// foo init is main false
// bar init is main false
// bar call is main false
// foo call is main true
我不确定这是错误还是预期行为,但对我来说似乎不正确(请参阅SR-15227)。
一种解决方法是始终确保初始化发生在@MainActor
上下文中,我们可以通过在我们第一次调用的地方注释闭包来做到这一点。
Task.detached { @MainActor in
await Foo.global.fooCall()
}
推荐阅读
- php - 将输入字段中的密码与数据库进行比较 | 拉拉维尔
- dns - DNS - 域 - 多个 A 记录
- python - 模块“pandas”没有属性“rolling_mean”
- c# - 在 Visual Studio 代码中找不到“GetReferenceNearestTargetFrameworkTask”任务
- json - 使用 swift 4.1 (通用处理程序 api) 调用 web api
- python - 将一个字符串连接到另一个字符串中
- java - Stax Provider 子类型问题 - Tomcat
- matlab - 如何对包含子分组数据的矩阵进行行排序
- sql - 如何在 WHERE 子句中的变量中使用 AND
- ios - 如何使用 json 和 swift 4.1 在谷歌地图上添加多个标记?