How to properly get rid of this concurrency compile error? - ios

#MainActor
class A {}
class VC: UIViewController {
let foo: A
init() {
self.foo = A() // Error: Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
super.init(nibName: nil, bundle: nil)
}
#MainActor
init() {
self.foo = A() // Compiles
super.init(nibName: nil, bundle: nil)
}
init(_ void: Void = ()) {
self.foo = A() // Compiles
super.init(nibName: nil, bundle: nil)
}
}
I get that A() should be called on a main-actor initializer, but why the first initializer doesn't inherit the #MainActor from UIViewController? and why the Void parameter gets rid of the error (even with -warn-concurrency)?
Note: if I annotate VC with #MainActor, I get the same error for the super.init in the first initializer, and the third initializer still works.

#MainActor limits all methods and attributes from being assessable from the main thread in the class A.
let foo: A is not the class A it an instance that stores a reference to the class A, you have done nothing to make setting foo thread safe. One of the init of VC is setup to only be executed in the main thread only, where foo is set, but another can be called by any thread and set foo as well.
Edit: Some more info I thought you might find useful
If your sole goal is thread safety, then using MainActor is not a good method, it means only the thread associated with GUI interaction can access your class, which could block some actual code that has to run in the main thread from executing, for just thread safety you are probable better of using actors, they work like classes, but only one thread can be in them at one time, they can be any thread, and different methods can be access by different threads a different times, but only one will be in but actor at a time.

You can also solve the problem by adding an initializer and marking it nonisolated.
#MainActor
class A {
nonisolated init() {}
func load() async -> String {
return "Async!"
}
}

Related

Running #MainActor in non isolated context with Thread.isMainThread check

I have a non-isolated property. Inside this I need to check whether it's being called from the main thread. If it is, then I want to immediately call a main actor isolated function. I have tried the following:
var myProperty: Bool {
if Thread.isMainThread {
performMainActorIsolatedFunction()
}
...
return true
}
However, it gives me the error:
Call to main actor-isolated instance method 'performMainActorIsolatedFunction()' in a synchronous nonisolated context.
How can I get this to work without marking myProperty with #MainActor?

Calling delegate methods either synchronously or asynchronously on a user provided queue

I am developing an API with its own delegate. I provide the caller a property to chose their own callback queue for the delegate methods.
The structure of my API class looks like:
class MyAPI {
weak var delegate: APIDelegate!
let delegateDispatchQueue: DispatchQueue
init(delegate: APIDelegate, delegateDispatchQueue: DispatchQueue) {
self.delegate = delegate
self.delegateDispatchQueue = delegateDispatchQueue
}
// public method definitions ...
}
While mostly I can call the delegate methods asynchronously, in some cases I need to call them synchronously. And that's where I seem to run into problems. If the user of my API calls my methods on the main thread, and they give the delegateDispatchQueue as the main queue, I get a crash when I try to call delegate methods synchronously.
Here is the helper class I'm using to dispatch my delegate calls to hopefully add a bit more flesh to this issue:
// Calls SyncServerDelegate methods on the `delegateDispatchQueue` either synchronously or asynchronously.
class Delegator {
private weak var delegate: SyncServerDelegate!
private let delegateDispatchQueue: DispatchQueue
init(delegate: SyncServerDelegate, delegateDispatchQueue: DispatchQueue) {
self.delegate = delegate
self.delegateDispatchQueue = delegateDispatchQueue
}
// All delegate methods must be called using this, to have them called on the client requested DispatchQueue. If sync is true, delegate method is effectively called synchronously on the `delegateDispatchQueue`. If sync is false, delegate method is called asynchronously on the `delegateDispatchQueue`.
func call(sync: Bool = false, callback: #escaping (SyncServerDelegate)->()) {
if sync {
// This is crashing with: Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
// seemingly because I am doing a sync dispatch on the main thread when I'm already on the main thread. The problem is, I can't compare threads/queues. https://stackoverflow.com/questions/17489098
delegateDispatchQueue.sync { [weak self] in
guard let self = self else { return }
callback(self.delegate)
}
}
else {
delegateDispatchQueue.async { [weak self] in
guard let self = self else { return }
callback(self.delegate)
}
}
}
}
My initial thought on a solution was to internally dispatch methods to another queue. Such as:
class MyAPI {
// ...
private let startQueue = DispatchQueue(label: "SyncServer", qos: .background)
public myAPIMethod() throws {
startQueue.async {
try myAPIMethodAux() // syntax error
}
}
}
but this is currently a non-starter because I am doing error handling in much of my code by throwing errors and the above pattern immediately generates a syntax error. I could re-write code without this form of error handling, but that's a big effort I'm not quite ready to take on.
Thoughts?
Update
I've not solved this yet, but am working around it. I've split my delegate methods into two parts. The main group of them I can call back asynchronously on delegateDispatchQueue. The other group, where I need to call them synchronously, I make no promises about what queue I call them on-- and just use the same queue that my API is currently running on.

Swift initializer call Dispatch Queue async

Is it wrong to call async from Swift object initializer such as this one
let serialQueue = DispatchQueue(label: "com.myApp.SerialQueue")
private let property1:Int?
public override init()
{
super.init()
/* Initialize properties */
setupProperties()
serialQueue.async { [unowned self] in
self.nonBlockingSetup()
}
}
private func setupProperties() {
self.property1 = 1
}
private func nonBlockingSetup() {
//Some non-blocking code that shouldn't run on main thread
}
Some people say async call is problematic before init returns. Need to know what Swift language says about it.
EDIT: Is there any difference if I modify the code as follows:
public override init()
{
super.init()
/* Initialize properties */
setupProperties()
callNonBlockingCodeAsync()
}
private func callNonBlockingCodeAsync() {
serialQueue.async { [unowned self] in
self.nonBlockingSetup()
}
}
To answer your question, I tried out the simple example.
Errors are very much self explanatory, in the initialisation process dispatchQueue are capturing self reference right before it's actual initialisation.
You are running into the concurrency problem where initialisation of object is necessary before using it.
dispatchQueue uses closures to provide DispatchWorkItem and as you know closures captures values surrounding it's scope.
Update
One work around would be to give default values to your properties but
I am not sure if that will help you.
In general, a constructor should not do any meaningful work.
Having a constructor that executes code delayed (because it's async) will be unexpected for anyone using that class (quite possibly including you in 6 months), and can therefore lead to bugs. In such cases it's usually better to have a separate initialization method, which makes it clear to an api user that there is something more going on.
If you absolutely want to make sure the initialization method is called, I usually make the constructor private and add a class method for construction. Again this signals api users that there is something going on behind the scenes.

Swift, why don't class methods need closure lists

If functions are essentially closures. Why don't methods of a class need closure lists when referencing self or another instance property within the closure.
Is there a [unowned self] behind the scenes? For example:
class MyClass{
func myFunc(){
self.otherFunc()
}
func otherFunc(){
print()
}
}
Wouldn't there be a reference cycle within myFunc? Ie, the closure is pointing to self, and the instance is pointing to the function. Neither could be deallocated.
"If functions are essentially closures." This isn't true. Functions (and methods) are not the same thing as closures. Functions have all their free variables unbound. Closures have bound some or all of their free variables (closed over them, which is where the name "closure" comes from).
A "free variable" is any variable defined outside the scope of the function (including its formal parameters). The top-level function func f(x: Int) has one free variable; when you call it, you must pass a parameter. A closure like { f(1) } has no free variables. When you call it, you do not pass any parameters.
A method, like a function, does not capture anything. It is passed all of its free variables when it is executed. For example, when you make the call object.doThis(), this is the same as calling Type.doThis(object)().
class X {
func doThis() {}
}
let x = X()
x.doThis()
X.doThis(x)() // Same thing
X.doThis(x) is a function that returns a function. There's no magic here. All the free variables are provided during the call. Nothing is captured. (The "free variable" in the case you describe is self, but that doesn't change anything. self is not special, except that it gets a little syntactic sugar around it.)
This is different than a closure:
let c = { x.doThis() }
c()
When I call c(), how does it know the value of x? I may have returned c and x may be out of scope now. The system has to keep track of x (including making a strong reference so it doesn't deallocate), and it does that by capturing it, or "closing over x" which raises the possibility of retain loops. So in c, x is bound. It is not free. You can't pass it when you call c().
self is not special here. It's just another variable. [weak self] in closures isn't special either. You can write [weak x] just as well. The [...] syntax is just the capture list.
Closures may only cause reference cycles when the closure is kept alive. Consider this:
let foo = MyClass()
let bar: () -> () = { in
print(foo)
}
The bar closure holds a reference to foo, but that reference goes away once nothing references bar anymore. For instance:
func f(foo: MyClass) {
let bar: () -> () = { () in
print(foo)
}
}
This does not create a reference cycle, because when f returns, the closure in barĀ is destroyed. Similarly, when you call myFunc and otherFunc, you do need a strong reference to self (the compiler ensures that you have it), but as you no longer need it at the end of the function, no cycle is created.
In general, a closure will not systematically create a reference cycle, even if it is #escaping. Consider the case of Dispatch.async:
class MyClass {
func foo() {
DispatchQueue.main.async {
print(self)
}
}
}
This does not actually create a reference cycle, because even though the closure references self for a while, self does not reference the closure.
The dangerous case is this one:
class MyClass {
var closure: () -> ()
func f() {
self.closure = {
print(self)
}
}
}
This one actually creates a reference cycle: self.closure has a strong reference to self, and self has a strong reference to self.closure.

Closure cannot implicitly capture a mutating self parameter

I am using Firebase to observe event and then setting an image inside completion handler
FirebaseRef.observeSingleEvent(of: .value, with: { (snapshot) in
if let _ = snapshot.value as? NSNull {
self.img = UIImage(named:"Some-image")!
} else {
self.img = UIImage(named: "some-other-image")!
}
})
However I am getting this error
Closure cannot implicitly capture a mutating self parameter
I am not sure what this error is about and searching for solutions hasn't helped
The short version
The type owning your call to FirebaseRef.observeSingleEvent(of:with:) is most likely a value type (a struct?), in which case a mutating context may not explicitly capture self in an #escaping closure.
The simple solution is to update your owning type to a reference once (class).
The longer version
The observeSingleEvent(of:with:) method of Firebase is declared as follows
func observeSingleEvent(of eventType: FIRDataEventType,
with block: #escaping (FIRDataSnapshot) -> Void)
The block closure is marked with the #escaping parameter attribute, which means it may escape the body of its function, and even the lifetime of self (in your context). Using this knowledge, we construct a more minimal example which we may analyze:
struct Foo {
private func bar(with block: #escaping () -> ()) { block() }
mutating func bax() {
bar { print(self) } // this closure may outlive 'self'
/* error: closure cannot implicitly capture a
mutating self parameter */
}
}
Now, the error message becomes more telling, and we turn to the following evolution proposal was implemented in Swift 3:
SE-0035: Limiting inout capture to #noescape contexts
Stating [emphasis mine]:
Capturing an inout parameter, including self in a mutating
method, becomes an error in an escapable closure literal, unless the
capture is made explicit (and thereby immutable).
Now, this is a key point. For a value type (e.g. struct), which I believe is also the case for the type that owns the call to observeSingleEvent(...) in your example, such an explicit capture is not possible, afaik (since we are working with a value type, and not a reference one).
The simplest solution to this issue would be making the type owning the observeSingleEvent(...) a reference type, e.g. a class, rather than a struct:
class Foo {
init() {}
private func bar(with block: #escaping () -> ()) { block() }
func bax() {
bar { print(self) }
}
}
Just beware that this will capture self by a strong reference; depending on your context (I haven't used Firebase myself, so I wouldn't know), you might want to explicitly capture self weakly, e.g.
FirebaseRef.observeSingleEvent(of: .value, with: { [weak self] (snapshot) in ...
Sync Solution
If you need to mutate a value type (struct) in a closure, that may only work synchronously, but not for async calls, if you write it like this:
struct Banana {
var isPeeled = false
mutating func peel() {
var result = self
SomeService.synchronousClosure { foo in
result.isPeeled = foo.peelingSuccess
}
self = result
}
}
You cannot otherwise capture a "mutating self" with value types except by providing a mutable (hence var) copy.
Why not Async?
The reason this does not work in async contexts is: you can still mutate result without compiler error, but you cannot assign the mutated result back to self. Still, there'll be no error, but self will never change because the method (peel()) exits before the closure is even dispatched.
To circumvent this, you may try to change your code to change the async call to synchronous execution by waiting for it to finish. While technically possible, this probably defeats the purpose of the async API you're interacting with, and you'd be better off changing your approach.
Changing struct to class is a technically sound option, but doesn't address the real problem. In our example, now being a class Banana, its property can be changed asynchronously who-knows-when. That will cause trouble because it's hard to understand. You're better off writing an API handler outside the model itself and upon finished execution fetch and change the model object. Without more context, it is hard to give a fitting example. (I assume this is model code because self.img is mutated in the OP's code.)
Adding "async anti-corruption" objects may help
I'm thinking about something among the lines of this:
a BananaNetworkRequestHandler executes requests asynchronously and then reports the resulting BananaPeelingResult back to a BananaStore
The BananaStore then takes the appropriate Banana from its inside by looking for peelingResult.bananaID
Having found an object with banana.bananaID == peelingResult.bananaID, it then sets banana.isPeeled = peelingResult.isPeeled,
finally replacing the original object with the mutated instance.
You see, from the quest to find a simple fix it can become quite involved easily, especially if the necessary changes include changing the architecture of the app.
If someone is stumbling upon this page (from search) and you are defining a protocol / protocol extension, then it might help if you declare your protocol as class bound. Like this:
protocol MyProtocol: class {
...
}
You can try this! I hope to help you.
struct Mutating {
var name = "Sen Wang"
mutating func changeName(com : #escaping () -> Void) {
var muating = self {
didSet {
print("didSet")
self = muating
}
}
execute {
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 15, execute: {
muating.name = "Wang Sen"
com()
})
}
}
func execute(with closure: #escaping () -> ()) { closure() }
}
var m = Mutating()
print(m.name) /// Sen Wang
m.changeName {
print(m.name) /// Wang Sen
}
Another solution is to explicitly capture self (since in my case, I was in a mutating function of a protocol extension so I couldn't easily specify that this was a reference type).
So instead of this:
functionWithClosure(completion: { _ in
self.property = newValue
})
I have this:
var closureSelf = self
functionWithClosure(completion: { _ in
closureSelf.property = newValue
})
Which seems to have silenced the warning.
Note this does not work for value types so if self is a value type you need to be using a reference type wrapper in order for this solution to work.

Resources