在这个post中,很好地解释了如何在Swift中实现Singletons,基本上可以用两行来完成:
class TheOneAndOnlyKraken {
static let sharedInstance = TheOneAndOnlyKraken()
private init() {} //This prevents others from using the default '()' initializer for this class.
}
但是,如果我的Singleton应该用一些数据进行初始化会怎么样?也许它需要封装API密钥或其他只能从外部接收的数据。示例可能如下所示:
class TheOneAndOnlyKraken {
let secretKey: String
static let sharedInstance = TheOneAndOnlyKraken()
private init() {} //This prevents others from using the default '()' initializer for this class.
}
在这种情况下,我们不能将初始化程序设为私有,因为我们必须创建一个初始化程序,它将String
作为参数来满足编译器:
init(secretKey: String) {
self.secretKey = secretKey
}
如何保存,我们仍然确保我们有单例的线程安全实例化?有没有办法避免使用dispatch_once
或者我们必须基本上默认回到Objective-C方式,我们使用dispatch_once
来确保初始化程序确实只被调用一次?
答案 0 :(得分:1)
首先,请注意您所暗示的ObjC方式不是线程正确的。它可能是安全的"因为它不会崩溃并且不会产生未定义的行为,但它会默默地忽略具有不同配置的后续初始化。这不是预期的行为。已知在写入后发生的读取器将不会接收写入的数据。这不符合一致性。因此,抛开这种模式是正确的理论。
那么什么是正确的?正确就是这样的:
import Dispatch
class TheOneAndOnlyKraken {
static let sharedInstanceQueue: DispatchQueue = {
let queue = DispatchQueue(label: "kraken")
queue.suspend()
return queue
}()
private static var _sharedInstance: TheOneAndOnlyKraken! = nil
static var sharedInstance: TheOneAndOnlyKraken {
var result: TheOneAndOnlyKraken!
sharedInstanceQueue.sync {
result = _sharedInstance
}
return result
}
// until this is called, all readers will block
static func initialize(withSecret secretKey: String) {
// It is a programming error to call this twice. If you want to be able to change
// it, you'll need another queue at least.
precondition(_sharedInstance == nil)
_sharedInstance = TheOneAndOnlyKraken(secretKey: secretKey)
sharedInstanceQueue.resume()
}
private var secretKey: String
private init(secretKey: String) {
self.secretKey = secretKey
}
}
这需要对TheOneAndOnlyKraken.intialize(withSecret:)
进行一次显式调用。在有人拨打该电话之前,sharedInstance
的所有请求都将被阻止。第二次调用initialize
会崩溃。