swift - 在swift dispatch_once单一实例模型

  显示原文与译文双语对照的内容

我正在尝试为Swift使用一个合适的单一模式。 到目前为止,我能够得到一个non-thread安全模型,如下所示:


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

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

 return Static.instance!
 }
}

在静态结构中包装单一实例应该允许一个单独的实例,该实例不会与单一的命名schemings冲突,并且不会产生复杂的命名。 显然,这个模型不是线程安全的,所以我尝试将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行上我得到了一个编译器错误:


Cannot convert the expression's type 'Void' to type '()'

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


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

任何人都知道 dispatch_once 使用swift的正确用法? 我最初认为问题是由于错误消息中的() 导致的问题,但更多的是,我认为这可能是获得 dispatch_once_t 正确定义的问题。

时间:

tl;博士︰使用类恒方法如果你使用的迅速 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
 }
}

这里我们使用嵌套结构的静态常量作为类常量。 这是一种替代静态类常量要放在快捷 1.1并提前激活,还能用的不足,作为一种解决方案的不足,提出静态中的常量和变量的功能。

dispatch_once

移植到Swift的传统 objective-c 方法。 在语法中interesting,我比较确定没有比二者之间嵌套结构体的方法,但我是在把它无论如何,因为我发现这里的一些优越


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的web 项目。

由于苹果现在已经明确了静态结构变量在 dispatch_once ( 查看文章末尾的便笺) 中被初始化和封装,我认为我的最终解决方案将是:


class WithSingleton {
 class var sharedInstance :WithSingleton {
 struct Singleton {
 static let instance = WithSingleton()
 }

 return Singleton.instance
 }
}

这利用了自动惰性的thread-safe初始化静态结构元素,安全地隐藏了来自使用者的实际实现,使所有的实现保持简洁,并消除了可见的全局变量。

苹果已经明确了懒惰的初始值设定是 thread-safe,所以不需要 dispatch_once 或者类似的保护

全局变量( 结构和枚举的静态成员)的惰性初始值设定项在全局访问时运行,并作为dispatch_once启动,以确保初始化是原子的。 这将使你能够在代码中使用 dispatch_once: 只需声明一个带有初始值设定项的全局变量并将它的标记为私有。

来自

于Xcode相关测试版 5, 编辑

最近对 Swift的更改,大多是新的访问控制方法。 我现在倾向于使用一个全局变量的全局变量。


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

就像 Swift 博客文章中提到的,:

全局变量( 结构和枚举的静态成员)的惰性初始值设定项在全局访问时运行,并作为dispatch_once启动,以确保初始化是原子的。 这将使你能够在代码中使用 dispatch_once: 只需声明一个带有初始值设定项的全局变量并将它的标记为私有。

这种创建singleton的方式是线程安全,快速,惰性的,而且也可以通过桥接向ObjC开放。

有更好的方法来做。 你可以在类的上面声明一个全局变量,就像这样


var tpScopeManagerSharedInstance = TPScopeManager()

这只调用默认的init或者任何初始化变量( 默认情况下是 dispatch_once ) 。 然后在你想要获得引用的类中,你只需执行以下操作:


var refrence = tpScopeManagerSharedInstance
//or you can just access properties and call methods directly
tpScopeManagerSharedInstance.someMethod()

所以基本上你可以去掉整个共享实例代码。

Swift 1.2现在支持类中的静态变量/常量。 所以你可以使用静态常量:


class MySingleton {

 static let sharedMySingleton = MySingleton()

 private init() {
//...
 }
}

Swift单例在 Cocoa 框架中作为类函数 比如 NSFileManager.defaultManager() 公开 NSNotificationCenter.defaultCenter() 因此,我觉得作为一个类函数来镜像这种行为更有意义,而不是像其他一些解决方案使用 e.g. 一样


private let _sharedInstance = MyClass()

class MyClass {
 class func sharedInstance() -> MyClass {
 return _sharedInstance
 }
}

通过 MyClass.sharedInstance() 检索 singleton

看一下苹果代码的样本,我发现了这个图案。 我不确定swift如何处理静态,但这在 C# 中是线程安全的。 我同时包括ObjC互操作的属性和方法。


struct StaticRank {
 static let shared = RankMapping()
}

class func sharedInstance() -> RankMapping {
 return StaticRank.shared
}

class var shared:RankMapping {
 return StaticRank.shared
}


class UtilSingleton: NSObject {

 var iVal: Int = 0

 class var shareInstance: UtilSingleton {
 get {
 struct Static {
 static var instance: UtilSingleton? = nil
 static var token: dispatch_once_t = 0
 }
 dispatch_once(&Static.token, {
 Static.instance = UtilSingleton()
 })
 return Static.instance!
 }
 }

}

如何使用:


UtilSingleton.shareInstance.iVal++
println("singleton new iVal = (UtilSingleton.shareInstance.iVal)")

在看到了david的实现之后,似乎没有一个单独的类函数 instanceMethod,因为 [let] 与sharedInstance类方法几乎一样。 你需要做的就是把它声明为一个全局常量,这就是它。


let gScopeManagerSharedInstance = ScopeManager()

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

我更喜欢这种实现:


class APIClient {

}

var sharedAPIClient: APIClient = {
 return APIClient()
}()

extension APIClient {
 class func sharedClient() -> APIClient {
 return sharedAPIClient
 }
}

...