提问人:David Berry 提问时间:6/4/2014 最后编辑:Nilanshu JaiswalDavid Berry 更新时间:8/3/2020 访问量:138077
在 Swift 中使用dispatch_once单例模型
Using a dispatch_once singleton model in Swift
问:
我正在尝试制定一个合适的单例模型,以便在 Swift 中使用。到目前为止,我已经能够获得一个非线程安全模型,其工作方式如下:
class var sharedInstance: TPScopeManager {
get {
struct Static {
static var instance: TPScopeManager? = nil
}
if !Static.instance {
Static.instance = TPScopeManager()
}
return Static.instance!
}
}
将单例实例包装在静态结构中应该允许单个实例不会与单例实例发生冲突,而不会出现复杂的命名策略,并且它应该使事情变得相当私密。但显然,这个模型不是线程安全的。所以我试着补充一下: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 1.2 及更高版本:
class Singleton {
static let sharedInstance = Singleton()
}
有了正确性的证明(所有的功劳都在这里),现在几乎没有理由对单例使用任何以前的方法。
更新:这是现在定义单例的官方方式,如官方文档中所述!
至于对使用与. 即使变量可用,也应该使用。单例不应被子类化,因为这将导致基本单例的多个实例。使用以一种美丽、迅捷的方式强制执行这一点。static
class
static
class
static
对于 Swift 1.0 和 1.1:
随着 Swift 最近的变化,主要是新的访问控制方法,我现在倾向于使用单例全局变量的更简洁的方式。
private let _singletonInstance = SingletonClass()
class SingletonClass {
class var sharedInstance: SingletonClass {
return _singletonInstance
}
}
正如 Swift 博客文章中提到的:
全局变量的惰性初始值设定项(也适用于 structs 和 enums) 在第一次访问 global 时运行,并且 作为dispatch_once启动,以确保初始化是 原子。这为在代码中使用 dispatch_once 提供了一种很酷的方式: 只需使用初始值设定项声明一个全局变量并标记它 私人。
这种创建单例的方式是线程安全的、快速的、懒惰的,并且还免费桥接到 ObjC。
评论
private init() {}
SingletonClass
查看 Apple 的示例代码,我遇到了这种模式。我不确定 Swift 如何处理静态,但这在 C# 中是线程安全的。我包括 Objective-C 互操作的属性和方法。
struct StaticRank {
static let shared = RankMapping()
}
class func sharedInstance() -> RankMapping {
return StaticRank.shared
}
class var shared:RankMapping {
return StaticRank.shared
}
评论
dispatch_once
class
static
有更好的方法可以做到这一点。您可以在类声明上方的类中声明一个全局变量,如下所示:
var tpScopeManagerSharedInstance = TPScopeManager()
这只调用你的默认 init 或 Swift 中默认的 init 和全局变量。然后,在你想获得引用的类中,你只需这样做:dispatch_once
var refrence = tpScopeManagerSharedInstance
// or you can just access properties and call methods directly
tpScopeManagerSharedInstance.someMethod()
因此,基本上您可以摆脱整个共享实例代码块。
评论
TPScopeManager.sharedInstance.doIt()
TPScopeManagerClass
public let TPScopeManager = TPScopeManagerClass()
TPScopeManager.doIt()
TPScopeManager
由于 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提供了一种很酷的方式:只需使用初始值设定项声明一个全局变量并将其标记为私有。
从这里开始
评论
private init() {}
用:
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.
}
评论
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 项目。
评论
init
private
final
init
private
我更喜欢这种实现:
class APIClient {
}
var sharedAPIClient: APIClient = {
return APIClient()
}()
extension APIClient {
class func sharedClient() -> APIClient {
return sharedAPIClient
}
}
如果你打算在 Objective-C 中使用 Swift 单例类,此设置将让编译器生成适当的类似 Objective-C 的头文件:
class func sharedStore() -> ImageStore {
struct Static {
static let instance : ImageStore = ImageStore()
}
return Static.instance
}
然后,在 Objective-C 类中,你可以像在 Swift 之前那样调用你的单例:
[ImageStore sharedStore];
这只是我的简单实现。
评论
NSFileManager.defaultManager()
我建议一个 ,就像你在 Java 中使用的那样,例如enum
enum SharedTPScopeManager: TPScopeManager {
case Singleton
}
评论
仅供参考,这里是 Jack Wu/hpique 的嵌套结构实现的 Singleton 实现示例。该实现还展示了存档的工作原理,以及一些附带的功能。我找不到这个完整的例子,所以希望这对某人有所帮助!
import Foundation
class ItemStore: NSObject {
class var sharedStore : ItemStore {
struct Singleton {
// lazily initiated, thread-safe from "let"
static let instance = ItemStore()
}
return Singleton.instance
}
var _privateItems = Item[]()
// The allItems property can't be changed by other objects
var allItems: Item[] {
return _privateItems
}
init() {
super.init()
let path = itemArchivePath
// Returns "nil" if there is no file at the path
let unarchivedItems : AnyObject! = NSKeyedUnarchiver.unarchiveObjectWithFile(path)
// If there were archived items saved, set _privateItems for the shared store equal to that
if unarchivedItems {
_privateItems = unarchivedItems as Array<Item>
}
delayOnMainQueueFor(numberOfSeconds: 0.1, action: {
assert(self === ItemStore.sharedStore, "Only one instance of ItemStore allowed!")
})
}
func createItem() -> Item {
let item = Item.randomItem()
_privateItems.append(item)
return item
}
func removeItem(item: Item) {
for (index, element) in enumerate(_privateItems) {
if element === item {
_privateItems.removeAtIndex(index)
// Delete an items image from the image store when the item is
// getting deleted
ImageStore.sharedStore.deleteImageForKey(item.itemKey)
}
}
}
func moveItemAtIndex(fromIndex: Int, toIndex: Int) {
_privateItems.moveObjectAtIndex(fromIndex, toIndex: toIndex)
}
var itemArchivePath: String {
// Create a filepath for archiving
let documentDirectories = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
// Get the one document directory from that list
let documentDirectory = documentDirectories[0] as String
// append with the items.archive file name, then return
return documentDirectory.stringByAppendingPathComponent("items.archive")
}
func saveChanges() -> Bool {
let path = itemArchivePath
// Return "true" on success
return NSKeyedArchiver.archiveRootObject(_privateItems, toFile: path)
}
}
如果你不认识其中的一些功能,这里有一个我一直在用的活生生的 Swift 实用程序文件:
import Foundation
import UIKit
typealias completionBlock = () -> ()
extension Array {
func contains(#object:AnyObject) -> Bool {
return self.bridgeToObjectiveC().containsObject(object)
}
func indexOf(#object:AnyObject) -> Int {
return self.bridgeToObjectiveC().indexOfObject(object)
}
mutating func moveObjectAtIndex(fromIndex: Int, toIndex: Int) {
if ((fromIndex == toIndex) || (fromIndex > self.count) ||
(toIndex > self.count)) {
return
}
// Get object being moved so it can be re-inserted
let object = self[fromIndex]
// Remove object from array
self.removeAtIndex(fromIndex)
// Insert object in array at new location
self.insert(object, atIndex: toIndex)
}
}
func delayOnMainQueueFor(numberOfSeconds delay:Double, action closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue()) {
closure()
}
}
我在 Swift 中的实现方式...
配置管理器.swift
import Foundation
let ConfigurationManagerSharedInstance = ConfigurationManager()
class ConfigurationManager : NSObject {
var globalDic: NSMutableDictionary = NSMutableDictionary()
class var sharedInstance:ConfigurationManager {
return ConfigurationManagerSharedInstance
}
init() {
super.init()
println ("Config Init been Initiated, this will be called only onece irrespective of many calls")
}
通过以下方式从应用程序的任何屏幕访问 globalDic。
读:
println(ConfigurationManager.sharedInstance.globalDic)
写:
ConfigurationManager.sharedInstance.globalDic = tmpDic // tmpDict is any value that to be shared among the application
第一个解决方案
let SocketManager = SocketManagerSingleton();
class SocketManagerSingleton {
}
在代码的后面:
func someFunction() {
var socketManager = SocketManager
}
第二种解决方案
func SocketManager() -> SocketManagerSingleton {
return _SocketManager
}
let _SocketManager = SocketManagerSingleton();
class SocketManagerSingleton {
}
稍后在代码中,您将能够保留大括号以减少混淆:
func someFunction() {
var socketManager = SocketManager()
}
Swift 单例在 Cocoa 框架中作为类函数公开,例如 、.因此,作为类函数来反映这种行为比作为类变量作为其他一些解决方案更有意义。例如:NSFileManager.defaultManager()
NSNotificationCenter.defaultCenter()
class MyClass {
private static let _sharedInstance = MyClass()
class func sharedInstance() -> MyClass {
return _sharedInstance
}
}
通过 检索单例。MyClass.sharedInstance()
评论
static
func init() -> ClassA {
struct Static {
static var onceToken : dispatch_once_t = 0
static var instance : ClassA? = nil
}
dispatch_once(&Static.onceToken) {
Static.instance = ClassA()
}
return Static.instance!
}
评论
dispatch_once
dispatch_once
Swift 1.2 或更高版本现在支持类中的静态变量/常量。因此,您可以只使用静态常量:
class MySingleton {
static let sharedMySingleton = MySingleton()
private init() {
// ...
}
}
根据 Apple 文档,已经多次重复,在 Swift 中执行此操作的最简单方法是使用静态类型属性:
class Singleton {
static let sharedInstance = Singleton()
}
但是,如果你正在寻找一种方法来执行简单的构造函数调用之外的其他设置,那么秘诀就是使用立即调用的闭包:
class Singleton {
static let sharedInstance: Singleton = {
let instance = Singleton()
// setup code
return instance
}()
}
这保证是线程安全的,并且只延迟初始化一次。
评论
final class MySingleton {
private init() {}
static let shared = MySingleton()
}
然后打电话给它;
let shared = MySingleton.shared
评论
init
private
sharedMyModel
final
sharedMyModel
shared
这是我的实现。它还会阻止程序员创建新实例:
let TEST = Test()
class Test {
private init() {
// This is a private (!) constructor
}
}
评论
简而言之,
class Manager {
static let sharedInstance = Manager()
private init() {}
}
您可能想阅读文件和初始化
全局变量的惰性初始值设定项(也适用于 structs 和 enums) 在第一次访问 global 时运行,并且 启动,以确保初始化是 原子。
dispatch_once
我要求我的单例允许继承,但这些解决方案实际上都不允许继承。所以我想出了这个:
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
- 首先执行此操作时,它将返回 created 的实例。
SubSingleton.sharedInstance()
SubSingleton
- 如果完成上述操作,则 is 为 true,并使用相同的实例。
SubSingleton.sharedInstance()
Singleton
第一种肮脏方法的问题在于,我不能保证子类会实现 并确保每个类只修改一次。dispatch_once_t
sharedInstanceVar
我将尝试进一步完善这一点,但看看是否有人对此有强烈的感觉会很有趣(除了它很冗长并且需要手动更新它之外)。
在 1.2 以上的 Swift 中,最好的方法是单行单例,因为 -
class Shared: NSObject {
static let sharedInstance = Shared()
private override init() { }
}
要了解有关此方法的更多详细信息,您可以访问此链接。
评论
斯威夫特 4+
protocol Singleton: class {
static var sharedInstance: Self { get }
}
final class Kraken: Singleton {
static let sharedInstance = Kraken()
private init() {}
}
评论
从 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
}()
}
这是具有线程安全功能的最简单的方法。任何其他线程都无法访问相同的单一实例对象,即使它们需要。斯威夫特 3/4
struct DataService {
private static var _instance : DataService?
private init() {} //cannot initialise from outer class
public static var instance : DataService {
get {
if _instance == nil {
DispatchQueue.global().sync(flags: .barrier) {
if _instance == nil {
_instance = DataService()
}
}
}
return _instance!
}
}
}
评论
我使用以下语法:
public final class Singleton {
private class func sharedInstance() -> Singleton {
struct Static {
//Singleton instance.
static let sharedInstance = Singleton()
}
return Static.sharedInstance
}
private init() { }
class var instance: Singleton {
return sharedInstance()
}
}
这适用于 Swift 1.2 到 4,并具有以下几个优点:
- 提醒用户不要子类实现
- 防止创建其他实例
- 确保延迟创建和唯一实例化
- 通过允许将实例作为
Singleton.instance
Swift 过去实现单例,无非是三种方式:全局变量、内部变量和dispatch_once方式。
这里有两个很好的单例。(注:无论哪种写作意志都必须注意私有化的init()方法。因为在 Swift 中,所有对象的构造函数默认都是 public,需要重写的 init 才能变成私有的,防止这个类的其他对象通过默认的初始化方法 '()' 来创建对象。
方法1:
class AppManager {
private static let _sharedInstance = AppManager()
class func getSharedInstance() -> AppManager {
return _sharedInstance
}
private init() {} // Privatizing the init method
}
// How to use?
AppManager.getSharedInstance()
方法2:
class AppManager {
static let sharedInstance = AppManager()
private init() {} // Privatizing the init method
}
// How to use?
AppManager.sharedInstance
唯一正确的方法如下。
final class Singleton {
static let sharedInstance: Singleton = {
let instance = Singleton()
// setup code if anything
return instance
}()
private init() {}
}
访问
let signleton = Singleton.sharedInstance
原因:
static
type 属性保证只延迟初始化一次,即使同时跨多个线程访问也是如此,因此无需使用dispatch_once
- 私有化方法,使实例不能由其他类创建。
init
final
类,因为您不希望其他类继承 Singleton 类。
评论
static let sharedInstance = Singleton()
在 swift 中,您可以按以下方式创建单例类:
class AppSingleton: NSObject {
//Shared instance of class
static let sharedInstance = AppSingleton()
override init() {
super.init()
}
}
斯威夫特 5.2
您可以使用 指向类型。所以:Self
static let shared = Self()
并且应该在一个类型中,例如:
class SomeTypeWithASingletonInstance {
static let shared = Self()
}
评论
@lazy
Static.instance = TPScopeManager()
Static.instance = self()