我正在努力制定一个合适的单例模型用于Swift的使用。到目前为止,我已经能够得到一个非线程安全的模型工作为:

class var sharedInstance: TPScopeManager {
    get {
        struct Static {
            static var instance: TPScopeManager? = nil
        }

        if !Static.instance {
            Static.instance = TPScopeManager()
        }

        return Static.instance!
    }
}

将单例实例包装在Static结构中应该允许单个实例不与单例实例发生冲突,而不需要复杂的命名方案,并且它应该使事情相当私密。但是,这个模型显然不是线程安全的。所以我尝试将dispatch_once添加到整个事情中:

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!
    }
}

但是我在dispatch_once行上得到了一个编译器错误:

不能将表达式的类型“Void”转换为类型“()”

我尝试了几种不同的语法变体,但它们似乎都有相同的结果:

dispatch_once(Static.token, { Static.instance = TPScopeManager() })

在Swift中dispatch_once的正确用法是什么?我最初认为问题出在错误消息中的()块上,但我看得越多,就越觉得可能是正确定义dispatch_once_t的问题。


当前回答

根据苹果的文档,已经重复了很多次,在Swift中最简单的方法是使用静态类型属性:

class Singleton {
    static let sharedInstance = Singleton()
}

然而,如果你正在寻找一种方法来执行一个简单的构造函数调用之外的额外设置,秘密是使用一个立即调用的闭包:

class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
        // setup code
        return instance
    }()
}

这保证是线程安全的,并且只进行一次惰性初始化。

其他回答

对于Swift 1.2及以上版本:

class Singleton  {
   static let sharedInstance = Singleton()
}

有了正确性的证明(所有功劳都在这里),现在几乎没有理由对单例对象使用任何前面的方法了。

更新:现在这是定义单例对象的官方方法,如官方文档中所述!

至于使用静态和类的问题。即使类变量可用,也应该使用静态变量。单例不应该被子类化,因为那样会导致基单例的多个实例。使用静态以一种漂亮的、Swifty的方式实现了这一点。

对于Swift 1.0和1.1:

随着Swift最近的变化,主要是新的访问控制方法,我现在倾向于对单例对象使用全局变量的更清洁的方式。

private let _singletonInstance = SingletonClass()
class SingletonClass {
  class var sharedInstance: SingletonClass {
    return _singletonInstance
  }
}

正如Swift博客文章中提到的:

的静态成员的惰性初始化式 Structs和enum)在第一次访问global时运行,并且 作为dispatch_once启动,以确保初始化为 原子。这为在代码中使用dispatch_once提供了一种很酷的方式: 只需声明一个带有初始化式的全局变量并标记它 私有的。

这种创建单例的方式是线程安全的、快速的、懒惰的,而且还可以免费桥接到ObjC。

从Apple Docs (Swift 3.0.1),

您可以简单地使用静态类型属性,它保证为 惰性初始化仅一次,即使跨多个访问 同时线程:

class Singleton {
    static let sharedInstance = Singleton()
}

如果需要执行初始化之外的其他设置,可以这样做 将闭包调用的结果赋值给全局变量 不变:

class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
        // setup code
        return instance
    }()
}

在Swift 1.2以上版本中,最好的方法是单行单例,如-

class Shared: NSObject {

    static let sharedInstance = Shared()

    private override init() { }
}

要了解更多关于这种方法的细节,请访问这个链接。

唯一正确的方法如下。

final class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
        // setup code if anything
        return instance
    }()

    private init() {}
}

访问

let signleton = Singleton.sharedInstance

原因:

静态类型属性保证只被惰性初始化一次,即使在多线程同时访问时也是如此,因此不需要使用dispatch_once 私有化init方法,使实例不能由其他类创建。 因为你不希望其他类继承Singleton类。

在看到David的实现之后,似乎没有必要使用单个类函数instanceMethod,因为let所做的事情与sharedInstance类方法几乎相同。你所要做的就是把它声明为一个全局常数,就是这样。

let gScopeManagerSharedInstance = ScopeManager()

class ScopeManager {
   // No need for a class method to return the shared instance. Use the gScopeManagerSharedInstance directly. 
}