once singleton model in Swift
I'm trying to work out an appropriate singleton model for usage in Swift. So far, I've been able to get a non-thread safe model working as:
class var sharedInstance:TPScopeManager {
get {
struct Static {
static var instance : TPScopeManager? = nil
}
if !Static.instance {
Static.instance = TPScopeManager()
}
return Static.instance!
}
}
Wrapping the singleton instance in the Static struct should allow a single instance that doesn't collide with singleton instances without complex naming schemings, and it should make things fairly private. Obviously though, this model isn't thread safe, so I tried to add dispatch_once to the whole thing:
class var sharedInstance:TPScopeManager {
get {
struct Static {
static var instance : TPScopeManager? = nil
static var token : dispatch_once_t = 0
}
dispatch_once(Static.token) { Static.instance = TPScopeManager() }
return Static.instance!
}
}
But I get a compiler error on the dispatch_once
line:
Cannot convert the expression's type 'Void' to type '()'
I've tried several different variants of the syntax, but they all seem to have the same results:
dispatch_once(Static.token, { Static.instance = TPScopeManager() })
What is the proper usage of dispatch_once
using Swift? I initially thought the problem was with the block due to the ()
in the error message, but the more I look at it, the more I think it may be a matter of getting the dispatch_once_t
correctly defined.
tl;dr: Use the class constant approach if you are using Swift 1.2 or above and the nested struct approach if you need to support earlier versions.
From my experience with Swift there are three approaches to implement the Singleton pattern that support lazy initialization and thread safety.
Class constant
class Singleton {
static let sharedInstance = Singleton()
}
This approach supports lazy initialization because Swift lazily initializes class constants (and variables), and is thread safe by the definition of let
. This is now officially recommended way to instantiate a singleton.
Class constants were introduced in Swift 1.2. If you need to support an earlier version of Swift, use the nested struct approach below or a global constant.
Nested struct
class Singleton {
class var sharedInstance: Singleton {
struct Static {
static let instance: Singleton = Singleton()
}
return Static.instance
}
}
Here we are using the static constant of a nested struct as a class constant. This is a workaround for the lack of static class constants in Swift 1.1 and earlier, and still works as a workaround for the lack of static constants and variables in functions.
dispatch_once
The traditional Objective-C approach ported to Swift. I'm fairly certain there's no advantage over the nested struct approach but I'm putting it here anyway as I find the differences in syntax interesting.
class Singleton {
class var sharedInstance: Singleton {
struct Static {
static var onceToken: dispatch_once_t = 0
static var instance: Singleton? = nil
}
dispatch_once(&Static.onceToken) {
Static.instance = Singleton()
}
return Static.instance!
}
}
See this GitHub project for unit tests.
Since Apple has now clarified that static struct variables are initialized both lazy and wrapped in dispatch_once (see the note at the end of the post), I think my final solution is going to be:
class WithSingleton {
class var sharedInstance :WithSingleton {
struct Singleton {
static let instance = WithSingleton()
}
return Singleton.instance
}
}
This takes advantage of the automatic lazy, thread-safe initialization of static struct elements, safely hides the actual implementation from the consumer, keeps everything compactly compartmentalized for legibility, and eliminates a visible global variable.
Apple has clarified that lazy initializer are thread-safe, so there's no need for dispatch_once
or similar protections
The lazy initializer for a global variable (also for static members of structs and enums) is run the first time that global is accessed, and is launched as dispatch_once to make sure that the initialization is atomic. This enables a cool way to use dispatch_once in your code: just declare a global variable with an initializer and mark it private.
From here
For Swift 1.2 and beyond:
class Singleton {
static let sharedInstance = Singleton()
}
With a proof of correctness (all credit goes here), there is little to no reason now to use any of the previous methods for singletons.
Update : This is now the official way to define singletons as described in the official docs!
As for concerns on using static
vs class
. static
should be the one to use even when class
variables become available. Singletons are not meant to be subclassed since that would result in multiple instances of the base singleton. Using static
enforces this in a beautiful, Swifty way.
For Swift 1.0 and 1.1:
With the recent changes in Swift, mostly new access control methods, I am now leaning towards the cleaner way of using a global variable for singletons.
private let _singletonInstance = SingletonClass()
class SingletonClass {
class var sharedInstance: SingletonClass {
return _singletonInstance
}
}
As mentioned in the Swift blog article here:
The lazy initializer for a global variable (also for static members of structs and enums) is run the first time that global is accessed, and is launched as dispatch_once to make sure that the initialization is atomic. This enables a cool way to use dispatch_once in your code: just declare a global variable with an initializer and mark it private.
This way of creating a singleton is thread safe, fast, lazy, and also bridged to ObjC for free.
链接地址: http://www.djcxy.com/p/13832.html上一篇: 我如何测试数组是否包含某个值?
下一篇: 曾经是Swift中的单身模特