How do I initialize a global variable with #MainActor? - swift

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.

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.
#MainActor
enum Globals {
static var foo = Foo()
}
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.
#MainActor
struct Foo {
static var shared = Foo()
}
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.
// somewhere early on
_ = Foo.shared
Bug in Swift 5.5, 5.6, 5.7
TL;DR: #MainActor sometimes won't call static let variables on the main thread. Use private(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.
#MainActor
func bar() {
print("bar is main", Thread.isMainThread)
}
#MainActor
struct Foo {
#MainActor
static let shared = Foo()
init() {
print("foo init is main", Thread.isMainThread)
bar()
}
func fooCall() {
print("foo call is main", Thread.isMainThread)
}
}
Task.detached {
await Foo.shared.fooCall()
}
// prints:
// foo init is main false
// bar is main false
// foo call is main true
This is a bug (see issue 58270).
A workaround is to always use a private(set) static var instead of a static let, as the correct isolation behaviour is enforced in that case.

Related

Actor-isolated property cannot be passed 'inout' to 'async' function call

I'm new (like most everyone, I suppose) to Swift concurrency, and I'm running into a compiler error I don't know what to do with.
struct Thing {
var counter = 0
mutating func increment() async {
counter += 1
}
}
class Controller: UIViewController {
var thing = Thing()
func mutate() async {
await thing.increment()
print(thing.counter)
}
}
let c = Controller()
Task {
await c.mutate()
}
The first line of the mutate() function gives me the following error.
Actor-isolated property 'thing' cannot be passed 'inout' to 'async' function call
If I just inherit from class instead of UIViewController things work fine, but I need the controller here, so I need to figure out how to make this work in that specific context.
I think the issue comes from Thing being a struct. A mutating func on a struct will assign a new value to the thing property on the Controller. In order for that to work, thing is treated as an inout parameter in the call to thing.increment().
If you make thing an actor instead of a struct then increment()won't need to be a mutating func and so thing won't be treated as an inout parameter.
A possible workaround is to make a copy of the struct first, then call the mutating func on the copy, then store it back on the property in the controller.
func mutate() async {
var thing = self.thing
await thing.increment()
self.thing = thing
print(thing.counter)
}
The reason it's an issue is the UIViewControllers are all actors now, so the properties are considered actor isolated. There is a nonisolated keyword but it cant be applied to stored properties so it doesn't seem to help here.
If the controller is changed to be an actor, the error message changes a bit to say that.
error: cannot call mutating async function 'increment()' on actor-isolated property 'thing'
await thing.increment()
^

How to implement custom implementation of method?

I'm drawing a blank for some reason.. If I want to make a bunch of objects from a class, but I want each instance to have its own unique implementation of a certain method, how would I do this?
For example:
class MyClass {
var name: String
func doSomething() {
// Each object would have custom implementation of this method, here.
}
}
Do I provide each object with its own closure during initialization, and then call that closure in the doSomething() method? I'm trying to figure out the correct or "Swiftly" way to do this. I'm also thinking along the lines of something with protocols, but I can't seem to figure out how to go about this.
I think there're many ways to do it.
In case of Base class + some sub-classes (e.g. Animal, subclassed by Dog, Cat, etc), you can do this:
First of all it's a good idea to define a protocol:
protocol MyProtocol {
func doSomething()
}
Also provide a default implementation, which throws a fatal error if a class doesn't override that method:
extension MyProtocol {
func doSomething() {
fatalError("You must override me")
}
}
Now your base class confirms the protocol thanks to default implementation. But it will throw a fatal error at runtime:
class MyClass: MyProtocol {
// conformant
}
Child class, however, will run correctly as long as it overrides this function:
class MyOtherClass: MyClass {
func doSomething() {
print("Doing it!")
}
}
You could also move fatal error into base class, and not do any extension implementation.
In case of many instances of the same Class, that solution makes no sense. You can use a very simple callback design:
typealias MyDelegate = () -> Void
class MyClass {
var delegate: MyDelegate?
func doSomething() {
delegate?()
}
}
let x = MyClass()
x.delegate = {
print("do it!")
}
x.doSomething()
// Or you can use a defined function
func doIt() {
print("also doing it")
}
x.delegate = doIt
x.doSomething()
It can also be that you re looking for Strategy pattern, or Template pattern. Depends on your usage details.
Do I provide each object with its own closure during initialization, and then call that closure in the doSomething() method
Yes. That is extremely common and eminently Swifty. Incredibly miminalistic example:
struct S {
let f:()->()
func doYourThing() { f() }
}
let s = S { print("hello") }
let s2 = S { print("goodbye" )}
s.doYourThing() // hello
s2.doYourThing() // goodbye
Giving an object a settable method instance property is very, very easy and common. It doesn't have to be provided during initialization — you might set this property later on, and a lot of built-in objects work that way too.
That, after all, is all you're doing when you create a data task with dataTask(with:completionHandler:). You are creating a data task and handing it a function which it stores, and which it will call when it has performed the actual networking.

Variable of type Self in static context

I have to modify an existing static method with return type Self.
I am using Self as it is required to work for subclasses of A as well. As the modification potentially needs a dispatch sync block to create the data for the returnee, I have to introduce a local variable of type Self.
Error:
'Self' is only available in a protocol or as the result of a method in a class;
class A {
//...
}
class B:A {
//...
}
extension A {
static private func foo() -> Self {
var myVar: Self? //Error: 'Self' is only available in a protocol or as the result of a method in a class;
// Get data for myVar, potentially in a dispatch sync block on another queue
guard let safeVar = myVar else {
return someGarbagr
}
return myVar
}
}
Intended usage:
func someFunctionSomewhere() {
let instanceOfB = B.foo()
// use instanceOfB
}
I have tried all I can think of already:
type(of:Self)
Self.Type
...
I would like to avoid modifying it to a generic method for several reasons. The main reason is that we would have to mention the type explicitly to make a generic version be able to refer the return type:
let instanceOfB: B = B.foo()

Accessing "self" in initializing closure

In Swift 3 the dispatch_once function was removed and the migration guide suggests to use initializing closure:
let myGlobal = { … global contains initialization in a call to a closure … }()
_ = myGlobal // using myGlobal will invoke the initialization code only the first time it is used.
I'd like to access 'self' instance variables from within the initializing closure like so:
class SomeClass {
var other = SomeOtherClass()
let initialize: () = {
// self.other - this doesn't work, complains about unresolved identifier 'self'
// how to access self.other here?
} ()
func doSomething() {
// initialize will only be called once
initialize
}
}
Why is 'self' not accessible in the closure and how can make it to be?
This quoted example of the Migration Guide is misleading because it's related to a global variable.
The closure of a instance let constant is called (once) immediately when the class is initialized. That's the reason why it cannot use other variables declared on the same level.
What you can do is to initialize initialize (the variable name is not the best one ;-) ) lazily. The closure is also called only once but – as the guide describes – only the first time (when) it is used.
class SomeClass {
let other = SomeOtherClass()
lazy var initialize : () = {
let test = self.other
test.doSomething()
}()
func doSomething() {
// initialize will only be called once
_ = initialize
}
}
When an instance of the 'SomeClass' class is created, it will first create all of the variables and constants on that instance. During this time, self may not be fully initialised, because it may be halfway through setting up. Because of this, self is not available until after the initialisation step has completed.
In the example, they were talking about a global variable which has no concept of self, or a static constant on the class which also has no concept of self.
If it needs to be an instance method/variable you could:
a) make it a lazy var like
lazy var initialise : ()->Void = {
return {
// can access self here
}
}()
which will be created the first time you call it, rather than during initialisation. Of course you lose the constant that way, and you have to store the closure which is wasteful since you're only executing it once.
b) put the code inside of an init method:
init() {
// if your class doesn't have a super class, you can access self.other here.
// If it does have a super class (like NSObject) you must first call super.init() here to complete the initialisation.
// This can only be done after all other variables have been set.
}

Calling function in initializer : Swift

I want to call a function in initializer, but i couldn't do it
class someclass {
var a : Int
var b : Int
init() {
self.a = 5
self.b = 4
func printx () {
println("you called the function")
}
printx()
}
}
var ab = someclass()
Is it posible to do that something like ab.init().printx
And
Will this printx() function execute when i initialize with someclass()
For your questions: No - you can't reference your printx from outside of init and Yes - your printx will execute when init is called. In Swift 1.2:
20> class Bar {
21. var ping : Int
22. init () {
23. ping = 1
24. func bar () { println ("bar") }
25. bar()
26. }
27. }
28> Bar()
bar
$R4: Bar = {
ping = 1
}
Defining your printx in init is perfectly viable and you should demand that it works (and it does). Having lexically contained function definitions in Swift is critically important. Now, if the language could allow the function name to be recursively visible...
Maybe you would do something like this?
class someclass {
var a : Int
var b : Int
func printx () {
println("you called the function")
}
init() {
self.a = 5
self.b = 4
printx()
}
}
var ab = someclass()
ab.printx()
Consider you never call init() writing it but just with someClass()
This question simply demonstrates a lack of understanding when it comes to scope.
The function printx() is not a method on the class SomeClass, but it a function scoped within the particular init() method you've defined.
As written, printx() will execute (and will print) when you call the init() method:
See the box in the right pane at the top? That's showing us all of the console output from our playground, exactly as it would display if we ran this otherwise.
However, we cannot now call ab.printx() because as I stated earlier, printx() is not a member of the class SomeClass. If we want to call printx() on instances of SomeClass, we must then define it as an instance method of the class (and if we still want to call it in init, we can).