Okay, so I found out about the new Swifty Dispatch API in Xcode 8. I'm having fun using DispatchQueue.main.async
, and I've been browsing around the Dispatch
module in Xcode to find all the new APIs.
But I also use dispatch_once
to make sure that things like singleton creation and one-time setup don't get executed more than once (even in a multithreaded environment)... and dispatch_once
is nowhere to be found in the new Dispatch module?
static var token: dispatch_once_t = 0 func whatDoYouHear() { print("All of this has happened before, and all of it will happen again.") dispatch_once(&token) { print("Except this part.") } }
In Swift, you can use lazily initialized globals or static properties and get the same thread-safety and called-once guarantees as dispatch_once provided. Example: let myGlobal: () = { … global contains initialization in a call to a closure … }()
Executes a block object only once for the lifetime of an application.
Since Swift 1.x, Swift has been using dispatch_once
behind the scenes to perform thread-safe lazy initialization of global variables and static properties.
So the static var
above was already using dispatch_once
, which makes it sort of weird (and possibly problematic to use it again as a token for another dispatch_once
. In fact there's really no safe way to use dispatch_once
without this kind of recursion, so they got rid of it. Instead, just use the language features built on it:
// global constant: SomeClass initializer gets called lazily, only on first use let foo = SomeClass() // global var, same thing happens here // even though the "initializer" is an immediately invoked closure var bar: SomeClass = { let b = SomeClass() b.someProperty = "whatever" b.doSomeStuff() return b }() // ditto for static properties in classes/structures/enums class MyClass { static let singleton = MyClass() init() { print("foo") } }
So that's all great if you've been using dispatch_once
for one-time initialization that results in some value -- you can just make that value the global variable or static property you're initializing.
But what if you're using dispatch_once
to do work that doesn't necessarily have a result? You can still do that with a global variable or static property: just make that variable's type Void
:
let justAOneTimeThing: () = { print("Not coming back here.") }()
And if accessing a global variable or static property to perform one-time work just doesn't feel right to you -- say, you want your clients to call an "initialize me" function before they work with your library -- just wrap that access in a function:
func doTheOneTimeThing() { justAOneTimeThing }
See the migration guide for more.
While the "lazy var" pattern allows me to stop caring about dispatch tokens and is generally more convenient than dispatch_once()
was, I don't like what it looks like at the call site:
_ = doSomethingOnce
I would expect this statement to look more like a function call (since it implies action), but it doesn't look so at all. Also, having to write _ =
to explicitly discard the result is annoying.
There is a better way:
lazy var doSomethingOnce: () -> Void = { print("executed once") return {} }()
Which makes the following possible:
doSomethingOnce()
This might be less efficient (since it calls an empty closure instead of just discarding a Void
), but improved clarity is totally worth it for me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With