如何在Swift中实现在initilisation上获取数据的Singleton?

时间:2016-09-22 23:36:21

标签: ios swift singleton

在这个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来确保初始化程序确实只被调用一次?

1 个答案:

答案 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会崩溃。