Is there an example of how dispatch_once should be used in Swift? (Preferably one from Apple.)
Note: In this case, I'm not using it for a singleton; I want to run arbitrary code exactly once.
Update: I'm mainly interested in the convention recommended when using this in an instance method, but usage in a class method, function, and in the global context would be useful for completeness sake.
Executes a block object only once for the lifetime of an application.
This function is useful for initialization of global data (singletons) in an application. Always call this function before using or testing any variables that are initialized by the block. If called simultaneously from multiple threads, this function waits synchronously until the block has completed.
dispatch_once() executes a block once and only once in a thread safe manner. Different threads that try to access the critical section — the code passed to dispatch_once — while a thread is already in this section are blocked until the critical section completes.
Grand Central Dispatch, a.k.a libdispatch and usually referred to as GCD, is a low-level API known for performing asynchronous background work.
dispatch_once_t
is type alias (Int
). Header documentation:
/*!
* @typedef dispatch_once_t
*
* @abstract
* A predicate for use with dispatch_once(). It must be initialized to zero.
* Note: static and global variables default to zero.
*/
typealias dispatch_once_t = Int
And here's the quote from dispatch_once
documentation:
The predicate must point to a variable stored in global or static scope. The result of using a predicate with automatic or dynamic storage (including Objective-C instance variables) is undefined.
Token variable must be stored in global / static scope and must be initialized to zero, which leads to this code:
import Foundation
var token: dispatch_once_t = 0
dispatch_once(&token) { () -> Void in
print("Called once")
}
It doesn't work if you omit = 0
(token
initialization), because compiler yields error Address of variable 'token' taken before it is initialized despite the fact that statics and globals default to zero. Tested in Xcode 7B2.
More examples based on comment. If you're inside class
method you have several possibilities.
You can't declare static property inside method otherwise compiler yields Static properties may only be declared on a type error. This doesn't work:
class func doItOnce() {
static var token: dispatch_once_t = 0
...
}
Must be declared on a type. This was introduced in Swift 1.2 (Xcode 6.3 IIRC).
“static” methods and properties are now allowed in classes (as an alias for “class final”). You are now allowed to declare static stored properties in classes, which have global storage and are lazily initialized on first access (like global variables). Protocols now declare type requirements as “static” requirements instead of declaring them as “class” requirements. (17198298)
So, what we can do if we don't like globals?
Static variable on a type
class MyClass {
private static var token: dispatch_once_t = 0
class func doItOnce() {
dispatch_once(&token) {
print("Do it once")
}
}
}
Static in a method wrapped in struct
Don't like static property on yur class? Would like to have it in your method? Wrap it in struct like this:
class func doItOnce() {
struct Tokens { static var token: dispatch_once_t = 0 }
dispatch_once(&Tokens.token) {
print("Do it once")
}
}
Actually I'm not aware of any Apple recommendation, best practice, ... how to do it for dispatch_once
. Simply use whatever you like most, feels good to you and just meet criteria global / static scope.
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