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
For those of you who are curious, for me this approach has been useful for this purpose:
class SomeVC : UIViewController {
private var token: dispatch_once_t = 0
public override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
dispatch_once(&token) { () -> Void in
self.doSomethingOnce()
}
}
}
By not declaring a static var it has the expected behaviour. That being said, this is definitely NOT RECOMMENDED for any serious project, since in the Docs (as your well said) it states:
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.
If we don't want to run into any future weird bugs and undefined behaviour I would just stick to what Apple says. But it's still nice to play around with these things, isn't it? =)
the robertvojta's answer is probably the best. because i try always to avoid import Foundation and use 'pure' Swift solution (with Swift3.0 i could change my opinion), I would like to share with you my own, very simple approach. i hope, this code is self-explanatory
class C {
private var i: Int?
func foo()->Void {
defer {
i = 0
}
guard i == nil else { return }
print("runs once")
}
}
let c = C()
c.foo() // prints "runs once"
c.foo()
c.foo()
let c1 = C()
c1.foo() // prints "runs once"
c1.foo()
class D {
static private var i: Int?
func foo()->Void {
defer {
D.i = 0
}
guard D.i == nil else { return }
print("runs once")
}
}
let d = D()
d.foo() // prints "runs once"
d.foo()
let d2 = D()
d.foo()
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.