Question: Can I set an optional trailing closure in a function?
Answer: Easiest way is to use a non-optional default value.
func doSomethingWithCompletion(completion: () -> Void = {}) { // ... Do Something ... completion() }
This popped up in a conversation yesterday and naturally the discussion then verged onto how efficient or inefficient this approach is over, say, passing an optional closure value, e.g. (() -> Void)?
.
So after running a few tens of millions of iterations on several approaches, the results were pretty interesting:
func test1(c : () -> Void) {c()} func test2(c: (() -> Void)?) { if let c = c {c()} } func test3(c: (() -> Void)?) {c?()} func test4(c: () -> Void = {}) {c()}
In unoptimized runs, both test1 and test4 massively out-performed test2 and test3 by an order of magnitude on 10 million increments of a local variable.
test1 Elapsed time: 0.134177029132843 After tests: 10000000 test2 Elapsed time: 1.11247903108597 After tests: 20000000 test3 Elapsed time: 0.944562971591949 After tests: 30000000 test4 Elapsed time: 0.146546959877014 After tests: 40000000 Program ended with exit code: 0
Using whole module optimization, the latter three are more or less equal but the first, without default values or optionals, still runs much faster.
test1 Elapsed time: 0.00389701128005981 After tests: 10000000 test2 Elapsed time: 0.0398470163345337 After tests: 20000000 test3 Elapsed time: 0.0474460124969482 After tests: 30000000 test4 Elapsed time: 0.0474919676780701 After tests: 40000000 Program ended with exit code: 0
If you’re going to supply arguments, make sure you use an in-clause for the default no-op closure, e.g. {_ in}
or {(_) in}
, etc.
One Comment
I’m actually more curious about the reasoning behind your answer to the actual question… 🙂 Why is specifying a default value considered easiest? Is it because you don’t have to specify `nil` when no closure is needed? Is it because calling the closure doesn’t have to use a question mark (`completion?()`)?