曾经是Swift中的单身模特
我正在试图找出一个适合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的经验,有三种方法可以实现支持延迟初始化和线程安全的Singleton模式。
类常量
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项目进行单元测试。
由于Apple现在已经阐明了静态结构变量被初始化为懒惰并包装在dispatch_once中(请参阅帖子末尾的注释),我认为我的最终解决方案是:
class WithSingleton {
class var sharedInstance :WithSingleton {
struct Singleton {
static let instance = WithSingleton()
}
return Singleton.instance
}
}
这利用了静态结构元素的自动惰性,线程安全的初始化,安全地隐藏了消费者的实际实现,保持了所有内容的紧凑性,并且消除了可见的全局变量。
Apple已经阐明了懒惰初始化程序是线程安全的,所以不需要dispatch_once
或类似的保护
全局变量的惰性初始化器(也适用于结构体和枚举的静态成员)在第一次访问全局时运行,并以dispatch_once的形式启动以确保初始化是原子性的。 这使得在你的代码中使用dispatch_once很酷的方式:只需用初始化程序声明一个全局变量并将其标记为私有。
从这里
对于Swift 1.2及更高版本:
class Singleton {
static let sharedInstance = Singleton()
}
有了正确性证明(所有功劳都在这里),现在几乎没有理由使用任何以前的单身方法。
更新 :现在这是在官方的文档中描述定义单身官方途径!
至于使用static
vs class
。 即使class
变量变为可用时, static
应该使用。 单身并不意味着被分类,因为这会导致基础单身的多个实例。 使用static
以一种美丽的Swifty方式强制执行此操作。
对于Swift 1.0和1.1:
随着Swift最近的变化,主要是新的访问控制方法,我现在倾向于为单身人士使用全局变量的更简洁的方式。
private let _singletonInstance = SingletonClass()
class SingletonClass {
class var sharedInstance: SingletonClass {
return _singletonInstance
}
}
正如Swift博客文章中提到的那样:
全局变量的惰性初始化器(也适用于结构体和枚举的静态成员)在第一次访问全局时运行,并以dispatch_once的形式启动以确保初始化是原子性的。 这使得在你的代码中使用dispatch_once很酷的方式:只需用初始化程序声明一个全局变量并将其标记为私有。
这种创建单例的方法是线程安全,快速,懒惰,并且也可以免费桥接到ObjC。
链接地址: http://www.djcxy.com/p/13831.html