I would like to have some sort of global variable that is synchronized using @MainActor
.
Here's an example struct:
@MainActor
struct Foo {}
I'd like to have a global variable something like this:
let foo = Foo()
However, this does not compile and errors with Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
.
Fair enough. I've tried to construct it on the main thread like this:
let foo = DispatchQueue.main.sync {
Foo()
}
This compiles! However, it crashes with EXC_BAD_INSTRUCTION
, because DispatchQueue.main.sync
cannot be run on the main thread.
I also tried to create a wrapper function like:
func syncMain<T>(_ closure: () -> T) -> T {
if Thread.isMainThread {
return closure()
} else {
return DispatchQueue.main.sync(execute: closure)
}
}
and use
let foo = syncMain {
Foo()
}
But the compiler does not recognize if Thread.isMainThread
and throws the same error message again, Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
.
What's the right way to do this? I need some kind of global variable that I can initialize before my application boots.
1条答案
按热度按时间4ngedf3f1#
One way would be to store the variable within a container (like an
enum
acting as an abstract namespace) and also isolating this to the main actor.An equally valid way would be to have a "singleton-like"
static
property on the object itself, which serves the same purpose but without the additional object.You now access the global object via
Foo.global
.One thing to note is that this will now be lazily initialized (on the first invocation) rather than immediately initialized. You can however force an initialization early on by making any access to the object.
Bug in Swift 5.5, 5.6, 5.7
TL;DR: @MainActor sometimes won't call
static let
variables on the main thread. Useprivate(set) static var
instead.While this compiles and works, it appears that this may call the initializer off the main thread and subsequently any calls made inside the initializer.
This is a bug (see issue 58270 ).
A workaround is to always use a
private(set) static var
instead of astatic let
, as the correct isolation behaviour is enforced in that case.