I discovered this weird behaviour:
Consider the following code:
func foo(_ x: () -> Void) {
}
class Bar {
func bar() {
print("hello")
}
}
var bar: Bar? = Bar()
These compiles:
foo(bar?.bar ?? {})
but this does not:
DispatchQueue.main.async(execute: bar?.bar ?? {})
It produces the error:
Cannot convert value of type '() -> ()' to expected argument type '@convention(block) () -> Void'
Now if I change bar to:
func bar() -> (() -> Void)? {
return { print("hello") }
}
and then do:
DispatchQueue.main.async(execute: bar() ?? {})
It compiles!
So it's definitely not because of the ?? operator
Question: Can you explain this behaviour?
Everything below this point is my experimentation.
I spotted that the only difference between the async method and foo is that the execute parameter is marked with @convention(block).
Hypothesis: I cannot pass optional chaining expressions into parameters marked with @convention(block).
But this seems to be wrong, as I can do this and it still doesn't compile:
DispatchQueue.main.async(execute: Bar().bar ?? {})
It also seems like I can cast the expression to a () -> Void and it magically compiles:
DispatchQueue.main.async(execute: (bar?.bar ?? {}) as () -> Void)
Isn't bar?.bar ?? {} already of type () -> Void? This makes no sense!
Another way to make it compile is to extract bar?.bar as a local variable:
let barFunc = bar?.bar
DispatchQueue.main.async(execute: barFunc ?? {})
which makes even less sense...