我正在努力制定一个合适的单例模型用于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的问题。
由于苹果现在已经澄清了静态结构变量是惰性初始化和包装在dispatch_once(见文章末尾的注释),我认为我的最终解决方案将是:
class WithSingleton {
class var sharedInstance: WithSingleton {
struct Singleton {
static let instance = WithSingleton()
}
return Singleton.instance
}
}
这利用了静态结构元素的自动惰性、线程安全初始化,对使用者安全地隐藏了实际的实现,保持所有内容紧凑地分隔以使其易于阅读,并消除了可见的全局变量。
苹果已经澄清了惰性初始化器是线程安全的,所以不需要dispatch_once或类似的保护
全局变量(也包括struct和enum的静态成员)的惰性初始化器在第一次访问global时运行,并作为dispatch_once启动,以确保初始化是原子的。这为在代码中使用dispatch_once提供了一种很酷的方式:只需声明一个带有初始化式的全局变量并将其标记为private。
从这里
我要求我的单例允许继承,而这些解决方案实际上都不允许。所以我想到了这个:
public class Singleton {
private static var sharedInstanceVar = Singleton()
public class func sharedInstance() -> Singleton {
return sharedInstanceVar
}
}
public class SubSingleton: Singleton {
private static var sharedInstanceToken: dispatch_once_t = 0
public class override func sharedInstance() -> SubSingleton {
dispatch_once(&sharedInstanceToken) {
sharedInstanceVar = SubSingleton()
}
return sharedInstanceVar as! SubSingleton
}
}
这样,当首先执行Singleton. sharedinstance()时,它将返回Singleton的实例
当首先执行SubSingleton. sharedinstance()时,它将返回创建的SubSingleton实例。
如果执行了上述操作,则SubSingleton.sharedInstance() is Singleton为true,并且使用相同的实例。
第一种脏方法的问题是,我不能保证子类会实现dispatch_once_t,并确保每个类只修改一次sharedInstanceVar。
我将尝试进一步完善它,但是看看是否有人对此有强烈的反对(除了它很冗长并且需要手动更新之外),这将是很有趣的。