我正在努力制定一个合适的单例模型用于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的问题。
tl;dr:如果你使用的是Swift 1.2或更高版本,使用类常量方法;如果你需要支持更早的版本,使用嵌套结构方法。
根据我使用Swift的经验,有三种方法来实现支持延迟初始化和线程安全的单例模式。
类常量
class Singleton {
static let sharedInstance = Singleton()
}
这种方法支持延迟初始化,因为Swift会延迟初始化类常量(和变量),并且通过let的定义是线程安全的。这是现在官方推荐的实例化单例的方法。
在Swift 1.2中引入了类常量。如果您需要支持早期版本的Swift,请使用下面的嵌套结构方法或全局常量。
嵌套的结构体
class Singleton {
class var sharedInstance: Singleton {
struct Static {
static let instance: Singleton = Singleton()
}
return Static.instance
}
}
这里我们使用嵌套结构的静态常量作为类常量。这是针对Swift 1.1及更早版本中缺少静态类常量的一种变通方法,并且仍然可以作为函数中缺少静态常量和变量的一种变通方法。
dispatch_once
将传统的Objective-C方法移植到Swift。我很确定这种方法与嵌套结构方法相比没有优势,但我还是把它放在这里,因为我发现语法上的差异很有趣。
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!
}
}
有关单元测试,请参阅此GitHub项目。
对于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。