我正在尝试制定一个适合在 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() })
dispatch_once
使用 Swift 的正确用法是什么?由于错误消息中的 ()
,我最初认为问题出在块上,但我越看它,我越认为这可能是正确定义 dispatch_once_t
的问题。
@lazy
的任何实现都应该是线程安全的。
Static.instance = TPScopeManager()
强制实例类型。如果您将 Static.instance = self()
之类的内容与必需的初始化程序一起使用,则会生成适当的类型类。即便如此,这是需要注意的重要一点,对于层次结构中的所有实例仅一次!要初始化的第一个类型是为所有实例设置的类型。我不认为objective-c 的行为是一样的。
tl;dr:如果您使用的是 Swift 1.2 或更高版本,请使用类常量方法;如果您需要支持早期版本,请使用嵌套结构方法。
根据我使用 Swift 的经验,有三种方法可以实现支持延迟初始化和线程安全的单例模式。
类常量
class Singleton {
static let sharedInstance = Singleton()
}
这种方法支持延迟初始化,因为 Swift 延迟初始化类常量(和变量),并且通过 let
的定义是线程安全的。现在是 officially recommended way 来实例化一个单例。
类常量是在 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 提供了一种很酷的方法:只需使用初始化程序声明一个全局变量并将其标记为私有。
来自here
private init() {}
,以进一步强制执行此类不打算在外部实例化的事实。
对于 Swift 1.2 及更高版本:
class Singleton {
static let sharedInstance = Singleton()
}
有了正确性证明(所有功劳归 here),现在几乎没有理由对单例使用任何以前的方法。
更新:这现在是定义单例的官方方式,如official docs中所述!
至于使用 static
与 class
的问题。即使 class
变量可用,也应该使用 static
。单例并不意味着被子类化,因为这会导致基单例的多个实例。使用 static
以一种美观、快速的方式强制执行此操作。
对于 Swift 1.0 和 1.1:
随着最近 Swift 的变化,主要是新的访问控制方法,我现在倾向于使用更简洁的方式来为单例使用全局变量。
private let _singletonInstance = SingletonClass()
class SingletonClass {
class var sharedInstance: SingletonClass {
return _singletonInstance
}
}
如 Swift 博客文章 here 中所述:
全局变量的惰性初始化器(也适用于结构和枚举的静态成员)在第一次访问全局时运行,并作为 dispatch_once 启动以确保初始化是原子的。这为在代码中使用 dispatch_once 提供了一种很酷的方法:只需使用初始化程序声明一个全局变量并将其标记为私有。
这种创建单例的方式是线程安全的、快速的、惰性的,并且还可以免费桥接到 ObjC。
private init() {}
添加为 SingletonClass
的初始化程序。以防止从外部实例化。
Swift 1.2 或更高版本现在支持类中的静态变量/常量。所以你可以只使用一个静态常量:
class MySingleton {
static let sharedMySingleton = MySingleton()
private init() {
// ...
}
}
有更好的方法来做到这一点。您可以在类声明上方的类中声明一个全局变量,如下所示:
var tpScopeManagerSharedInstance = TPScopeManager()
这只是调用您的默认 init 或在 Swift 中默认为 dispatch_once
的任何 init 和全局变量。然后在你想获得参考的任何课程中,你只需这样做:
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
的其他实例,因此根据定义,它不是单例。
Swift 单例在 Cocoa 框架中作为类函数公开,例如 NSFileManager.defaultManager()
、NSNotificationCenter.defaultCenter()
。因此,作为反映这种行为的类函数而不是作为其他解决方案的类变量更有意义。例如:
class MyClass {
private static let _sharedInstance = MyClass()
class func sharedInstance() -> MyClass {
return _sharedInstance
}
}
通过 MyClass.sharedInstance()
检索单例。
static
属性。
根据 Apple documentation,在 Swift 中执行此操作的最简单方法是使用静态类型属性已多次重复:
class Singleton {
static let sharedInstance = Singleton()
}
但是,如果您正在寻找一种方法来执行简单的构造函数调用之外的其他设置,那么秘诀就是使用立即调用的闭包:
class Singleton {
static let sharedInstance: Singleton = {
let instance = Singleton()
// setup code
return instance
}()
}
这保证是线程安全的并且只延迟初始化一次。
斯威夫特 4+
protocol Singleton: class {
static var sharedInstance: Self { get }
}
final class Kraken: Singleton {
static let sharedInstance = Kraken()
private init() {}
}
查看 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
吗?
dispatch_once
功能。
简单来说,
class Manager {
static let sharedInstance = Manager()
private init() {}
}
您可能想阅读Files and Initialization
全局变量的惰性初始化器(也适用于结构和枚举的静态成员)在第一次访问全局时运行,并作为 dispatch_once 启动以确保初始化是原子的。
如果您计划在 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()
,但仍使用 Swift 的惰性线程安全静态成员机制。
第一个解决方案
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()
}
final class MySingleton {
private init() {}
static let shared = MySingleton()
}
然后调用它;
let shared = MySingleton.shared
init
标记为 private
,而且将 sharedMyModel
标记为 final
!为了未来的读者,在 Swift 3 中,我们可能倾向于将 sharedMyModel
重命名为简单的 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)")
Swift 1.2 以上的最佳方法是单行单例,如 -
class Shared: NSObject {
static let sharedInstance = Shared()
private override init() { }
}
要了解有关此方法的更多详细信息,您可以访问此link。
NSObject
子类?除此之外,这似乎与 stackoverflow.com/a/28436202/1187415 基本相同。
来自 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
}()
}
我建议使用 enum
,就像您在 Java 中使用的那样,例如
enum SharedTPScopeManager: TPScopeManager {
case Singleton
}
仅供参考,这里是 Jack Wu/hpique 的嵌套结构实现的示例单例实现。该实现还显示了归档如何工作,以及一些附带的功能。我找不到这个完整的例子,所以希望这对某人有帮助!
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 中,您可以通过以下方式创建一个单例类:
class AppSingleton: NSObject {
//Shared instance of class
static let sharedInstance = AppSingleton()
override init() {
super.init()
}
}
我更喜欢这种实现:
class APIClient {
}
var sharedAPIClient: APIClient = {
return APIClient()
}()
extension APIClient {
class func sharedClient() -> APIClient {
return sharedAPIClient
}
}
我在 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
唯一正确的方法如下。
final class Singleton {
static let sharedInstance: Singleton = {
let instance = Singleton()
// setup code if anything
return instance
}()
private init() {}
}
访问
let signleton = Singleton.sharedInstance
原因:
静态类型属性保证只延迟初始化一次,即使跨多个线程同时访问,所以不需要使用 dispatch_once
私有化 init 方法,因此其他类不能创建实例。
final 类,因为您不希望其他类继承 Singleton 类。
static let sharedInstance = Singleton()
在看到 David 的实现之后,似乎没有必要使用单例类函数 instanceMethod
,因为 let
与 sharedInstance
类方法做的事情几乎相同。您需要做的就是将其声明为全局常量,就是这样。
let gScopeManagerSharedInstance = ScopeManager()
class ScopeManager {
// No need for a class method to return the shared instance. Use the gScopeManagerSharedInstance directly.
}
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
自动保护 Apple 实际上出于这个原因建议使用静态而不是 dispatch_once。
Swift过去实现单例,无外乎三种方式:全局变量、内部变量和dispatch_once方式。
这里有两个很好的singleton。(注意:无论哪种写法都要注意私有化的init()方法。因为在Swift中,所有对象的构造函数默认都是public的,需要重写init才能变成private , 防止这个类的其他对象 '()' 默认初始化方法创建对象。)
方法一:
class AppManager {
private static let _sharedInstance = AppManager()
class func getSharedInstance() -> AppManager {
return _sharedInstance
}
private init() {} // Privatizing the init method
}
// How to use?
AppManager.getSharedInstance()
方法二:
class AppManager {
static let sharedInstance = AppManager()
private init() {} // Privatizing the init method
}
// How to use?
AppManager.sharedInstance
斯威夫特 5.2
您可以使用 Self
指向类型。所以:
static let shared = Self()
并且应该在一个类型中,例如:
class SomeTypeWithASingletonInstance {
static let shared = Self()
}
这是具有线程安全功能的最简单的一种。没有其他线程可以访问相同的单例对象,即使他们想要。斯威夫特 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 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() 是 Singleton 为 true 并使用相同的实例。
第一种肮脏方法的问题是我不能保证子类会实现 dispatch_once_t
并确保每个类只修改一次 sharedInstanceVar
。
我将尝试进一步完善这一点,但看看是否有人对此有强烈的感受会很有趣(除了它很冗长并且需要手动更新它的事实之外)。
这是我的实现。它还阻止程序员创建新实例:
let TEST = Test()
class Test {
private init() {
// This is a private (!) constructor
}
}
private init
:stackoverflow.com/a/28436202/1187415。
我使用以下语法:
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 访问来缩短语法(避免 ())
init
声明为private
以保证该对象的一个且只有一个实例将在应用程序的整个生命周期中存在?final
,这样您就不会对其进行子类化; (b) 将init
方法声明为private
,这样您就不会意外地在某处实例化另一个实例。