在Swift中使用dispatch_once单例模型


575

我正在尝试制定一个合适的单例模型以在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网上遇到了一个编译器错误:

无法将表达式的类型“无效”转换为类型“()”

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

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

dispatch_once使用Swift 的正确用法是什么?最初,我认为问题是由于()错误消息中的块引起的,但是我看得越多,我就认为获得dispatch_once_t正确定义的问题就越多。


3
我将删除所有这些静态代码,并使用带有@lazy初始化程序的readonly属性。
Sulthan 2014年

1
我正是这个意思。不幸的是,我们仍然没有足够的内部信息。但是,恕我直言,任何实现都@lazy应该是线程安全的。
Sulthan

1
而且这种方式还具有不将实现暴露给调用者的优势的优点。
David Berry 2014年

1
似乎也没有@lazy类变量。
David Berry

小心!这种方法需要注意两件事。首先,任何继承自此的类都必须重写sharedInstance属性。Static.instance = TPScopeManager()强制实例类型。如果您将类似的东西Static.instance = self()与必需的初始化程序一起使用,则会生成适当的类型类。即使如此,这也是要注意的重要事项,对于层次结构中的所有实例,仅一次!初始化的第一个类型是为所有实例设置的类型。我认为Objective-C的表现不一样。
肖恩·伍德沃德

Answers:


713

tl; dr:如果使用的是Swift 1.2或更高版本,请使用类常量方法;如果需要支持早期版本,请使用嵌套的struct方法。

根据我在Swift中的经验,有三种方法可以实现支持延迟初始化和线程安全的Singleton模式。

类常数

class Singleton  {
   static let sharedInstance = Singleton()
}

这种方法支持延迟初始化,因为Swift会延迟地初始化类常量(和变量),并且通过的定义是线程安全的let。现在,这是正式推荐的实例化单例的方法。

在Swift 1.2中引入了类常量。如果您需要支持早期版本的Swift,请使用下面的嵌套struct方法或全局常量。

嵌套结构

class Singleton {
    class var sharedInstance: Singleton {
        struct Static {
            static let instance: Singleton = Singleton()
        }
        return Static.instance
    }
}

在这里,我们将嵌套结构的静态常量用作类常量。这是在Swift 1.1和更早版本中缺少静态类常量的解决方法,并且仍然可以在函数中缺少静态常量和变量的情况下使用。

一次派遣

传统的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项目进行单元测试。


13
“借助let保证线程安全”-是否在任何地方都提到了这一点?我在文档中找不到提及它的地方。
jtbandes 2014年

4
@jtbandes常量在我所知道的所有语言中都是线程安全的。
hpique 2014年

2
@DaveWood我假设您正在谈论最后一种方法。我会引用自己的话:“我说不再需要使用这种方法,但是我发现语法的差异很有趣,所以我还是把它放在这里。”
hpique 2014年

5
应该init也声明private,以保证有且只有一个实例对象将永远存在,整个应用程序的生命周期?
安德鲁

5
在“类常量”方法中,我建议(a)声明该类为final子类,这样您就不会对其进行子类化;(b)声明该init方法为private您不会在某处意外实例化另一个实例。
罗布

175

由于Apple现在已经澄清了静态结构变量既可以被懒惰也可以被包装dispatch_once(请参阅文章末尾的注释),因此,我认为我的最终解决方案是:

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

        return Singleton.instance
    }
}

这利用了静态结构元素的自动懒惰,线程安全的初始化,对使用者安全地隐藏实际实现,使所有内容紧凑地划分为易于辨认并消除了可见的全局变量的优势。

苹果已经澄清了惰性初始化器是线程安全的,因此不需要dispatch_once或没有类似的保护措施

全局变量(也适用于结构和枚举的静态成员)的惰性初始化程序在首次访问global时运行,并作为dispatch_once启动,以确保初始化是原子的。这提供了一种在代码中使用dispatch_once的好方法:只需使用初始化程序声明全局变量并将其标记为私有。

这里


1
确认一下:全局变量具有惰性的线程安全初始化,而类变量则没有。对?
比尔

14
我要补充一点,一个好的做法是将初始化程序声明为private private init() {}:,以进一步实施该类并非要在外部实例化的事实。
Pascal Bourque 2014年

1
因此,静态struct var初始化是惰性的并且是线程安全的,如果该静态struct var是多字节的字典,那我们必须为每次访问手动同步/排队对其调用,对吗?

如果我正确理解了您的问题,则字典和数组访问天生就不是线程安全的,因此您将需要使用某种形式的线程同步。
大卫·贝里

@DavidBerry我应该如何在此单例类中调用函数?我需要在myClass.sharedInstance的第一次调用中调用一个函数。
Ameet Dhas 2015年

163

对于Swift 1.2及更高版本:

class Singleton  {
   static let sharedInstance = Singleton()
}

有了正确性的证明(所有功劳都放在这里),现在几乎没有理由对单例使用任何先前的方法。

更新:这是官方文档中定义单例的官方方式!

至于使用staticvs的担忧classstatic即使class变量可用,也应该使用。单例不打算被子类化,因为这将导致基本单例的多个实例。使用static以一种美观,迅捷的方式强制执行此操作。

对于Swift 1.0和1.1:

随着Swift的最新变化(主要是新的访问控制方法),我现在倾向于使用更简单的方法来为单例使用全局变量。

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

随着斯威夫特博客文章中提到在这里

全局变量(也适用于结构和枚举的静态成员)的惰性初始化程序在首次访问global时运行,并作为dispatch_once启动,以确保初始化是原子的。这提供了一种在代码中使用dispatch_once的好方法:只需使用初始化程序声明全局变量并将其标记为私有。

这种创建单例的方法是线程安全,快速,懒惰的,并且还免费桥接到ObjC。


2
任何人都只能读这个答案:请记住要使令牌静态,否则行为是不确定的。有关完整代码,请参见David编辑的问题。
nschum 2014年

@nschum否则,该行为不是未定义的,而是以明确定义的方式被破坏的:该块将始终执行。
2014年

@Michael:文档指出它是未定义的。因此,当前行为是偶然的。
nschum 2014年

1
说的很奇怪。如果文档将其称为“未定义”,则仅表示编写该代码的人不会对其所作的任何承诺。它与知道变量是否为静态的代码无关。这只是意味着不能依靠当前(或表面上的)行为。
nschum 2014年

6
您可能要添加private init() {}作为的初始化程序SingletonClass。防止从外部实例化。
rintaro 2014年

46

Swift 1.2或更高版本现在支持类中的静态变量/常量。因此,您可以使用静态常量:

class MySingleton {

    static let sharedMySingleton = MySingleton()

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

35

有一种更好的方法。您可以在类声明上方的类中声明全局变量,如下所示:

var tpScopeManagerSharedInstance = TPScopeManager()

这只是调用您的默认init或dispatch_onceSwift中默认使用的init和全局变量。然后,无论您要获取参考的哪个类,都可以执行以下操作:

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

因此,基本上,您可以摆脱共享实例代码的整个块。


3
为什么要“变”,而要“变”?
斯蒂芬2014年

1
也许可以放手,我只用var进行了测试。
Kris Gellci 2014年

我喜欢这个答案,但是我需要从Interface Builder访问它(Singleton)。关于如何从IB中访问此tpScopeManagerSharedInstance的任何想法吗?谢谢。-–
Luis Palacios

这是我拥有单身人士的首选方式。它具有所有通常的功能(线程安全性和延迟实例化)并且支持非常轻量级的语法:无需一直编写TPScopeManager.sharedInstance.doIt(),只需命名您的类TPScopeManagerClass,在该类旁边放置此声明public let TPScopeManager = TPScopeManagerClass(),以及仅使用write即可TPScopeManager.doIt()。很干净!
亚历克斯(Alex)

这里没有阻止创建的其他实例的内容TPScopeManager,因此根据定义它不是单例
迦勒

28

迅速单身露出在Cocoa框架作为类功能,例如NSFileManager.defaultManager()NSNotificationCenter.defaultCenter()。因此,作为类函数来反映此行为更有意义,而不是像其他解决方案那样使用类变量。例如:

class MyClass {

    private static let _sharedInstance = MyClass()

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

通过检索单例MyClass.sharedInstance()


1
为LearnCocos2D :)的评论而投票,也为该样式而投票。
x4h1d 2015年

2
全局变量应通过类内部的静态变量更改为类变量。
malhal

2
@malhal当变量被标记为私有但在类外部时,它不是全局的-而是仅作用于其所在的文件。类内部的static几乎相同,但是我更新了答案以使用static如您建议的那样,因为如果您恰巧在文件中使用多个类,则可以更好地将变量分组到类中。
Ryan

1
“ Swift Singletons在可可框架中作为类函数公开”……在Swift 3中没有。它们现在通常是static属性。
罗布

17

根据Apple文档,已经多次重复说明,在Swift中最简单的方法是使用静态类型属性:

class Singleton {
    static let sharedInstance = Singleton()
}

但是,如果您正在寻找一种方法来执行除简单的构造函数调用之外的其他设置,那么秘诀就是使用立即调用的闭包:

class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
        // setup code
        return instance
    }()
}

这保证是线程安全的,并且仅延迟一次初始化。


如何将静态let实例设置回nil?
gpichler

1
@ user1463853-您不能,而且通常不应该。
罗布(Rob)2016年

16

迅捷4+

protocol Singleton: class {
    static var sharedInstance: Self { get }
}

final class Kraken: Singleton {
    static let sharedInstance = Kraken()
    private init() {}
}

2
这需要最后一堂课,您能否解释更多区别,因为我对带有结构的单例的其他解决方案有疑问
Raheel Sadiq

应该是私有重写init(){}
NSRover

8

在查看苹果的示例代码时,我遇到了这种模式。我不确定Swift如何处理静态,但这在C#中是线程安全的。我同时包含了Objective-C互操作的属性和方法。

struct StaticRank {
    static let shared = RankMapping()
}

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

class var shared:RankMapping {
    return StaticRank.shared
}

我非常确定,仅使用此默认静态语法即可完成所有烦人的工作。
Eonil 2014年

不幸的是,静态仅在结构内部起作用,因此这就是这种模式的原因。
user2485100 2014年

我的意图是我们不必使用dispatch_once东西。我相信你的风格。:)
Eonil 2014年

是不是class一类声明中相当于static在结构声明?
罗素·博罗戈夫

@Sam是的。请参阅有关文件和初始化的Apple博客条目,该条目清楚表明结构和枚举的全局成员和静态成员都将从此dispatch_once功能中受益。
罗布

5

简单来说,

class Manager {
    static let sharedInstance = Manager()
    private init() {}
}

您可能需要阅读文件和初始化

全局变量(也适用于结构和枚举的静态成员)的惰性初始化程序在首次访问global时运行,并启动dispatch_once以确保初始化是原子的。


4

如果您打算在Objective-C中使用Swift Singleton类,则此设置将使编译器生成适当的类似于Objective-C的标头:

class func sharedStore() -> ImageStore {
struct Static {
    static let instance : ImageStore = ImageStore()
    }
    return Static.instance
}

然后在Objective-C课程中,您可以像在Swift之前的日子那样称呼您的单身人士:

[ImageStore sharedStore];

这只是我的简单实现。


实际上,这比其他示例更简洁,更正确,因为它的实现方式与其他Swift单例相同。即:作为类的功能一样NSFileManager.defaultManager(),但仍然使用雨燕的懒惰线程安全的静态成员的机制。
莱斯利·戈德温

如今,可可通常将其实现为静态属性,而不是作为类函数。
罗布

我知道,我的评论已经超过2年了。感谢您的提及。
迈克尔

4

第一个解决方案

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()        
}

4
final class MySingleton {
     private init() {}
     static let shared = MySingleton()
}

然后调用它;

let shared = MySingleton.shared

做得好,不仅标记init为as private,而且标记为sharedMyModelas final!为了将来的读者起见,在Swift 3中,我们可能倾向于将其重命名sharedMyModel为simple shared
罗布

这是唯一正确的答案,只是对super.init的重写和调用是错误的,甚至无法编译。
Michael Morris

4

采用:

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 Berry

@David除了没有全局变量。:)
hpique 2014年

@hpique不,完全像我以前的尝试之一。查看编辑历史记录。
David Berry

4

Swift中高于1.2的最佳方法是单行单例,因为-

class Shared: NSObject {

    static let sharedInstance = Shared()

    private override init() { }
}

要了解有关此方法的更多详细信息,请访问此链接


为什么是NSObject子类?除此之外,这似乎与stackoverflow.com/a/28436202/1187415基本相同。
马丁·R

3

在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

我建议使用enum,就像您在Java中使用的那样,例如

enum SharedTPScopeManager: TPScopeManager {
    case Singleton
}

IMO,这是实现Singleton的唯一正确的Swift方法。其他答案是ObjC / C / C ++方式
Bryan Chen

您能否详细说明这个答案?我不清楚从此片段中实例化Singleton的位置
Kenny Winker 2014年

@KennyWinker我没有Apple开发人员登录名,因此没有很快,所以初始化时我无法回答。在Java中,它是首次使用。也许您可以在初始化时通过打印尝试一下,看看打印是在启动时还是在访问后进行。这将取决于编译器如何实现枚举。
Howard Lovatt 2014年

@KennyWinkler:苹果公司已经澄清了它的工作原理,请参阅developer.apple.com/swift/blog/?id=7。在其中,他们说“特别是在首次引用全局初始化程序时,类似于Java一样”。他们还说,在幕后他们使用“ dispatch_once来确保初始化是原子的”。因此,除非您有一些奇特的init要做,否则几乎可以肯定,枚举是必经之路,那么私有静态let是解决方案。
霍华德·洛瓦特

2

仅供参考,下面是Jack Wu / hpique的Nested Struct实现的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()
    }
}


1

我更喜欢这种实现:

class APIClient {

}

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

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

1

我在Swift中的实现方式...

ConfigurationManager.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

1

唯一正确的方法如下。

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()
abhimuralidharan

1
如果您不想执行任何其他设置,那么您说的是正确的。
applefreak

1

看完David的实现后,似乎不需要单例类函数,instanceMethod因为let它与sharedInstance类方法几乎一样。您需要做的就是将其声明为全局常量,仅此而已。

let gScopeManagerSharedInstance = ScopeManager()

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

2
正如我在评论中所说,这样做的唯一原因是,将来某个时候您可以移动/隐藏全局变量并获得更多类似单例的行为。到那时,如果一切都使用一致的模式,则只需更改单例类本身,而不必更改用法。
David Berry 2014年

0
   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,因此不必立即包装初始化。
David Berry 2015年

0

Swift过去实现单例的方法无非是三种方式:全局变量,内部变量和dispatch_once方式。

这是两个很好的单例。(注意:无论哪种写作都必须注意私有化的init()方法。由于在Swift中,所有对象的构造函数默认值都是public,因此需要重写init可以变成private ,默认情况下阻止此类“()”的其他对象创建该对象。)

方法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

-1

这是具有线程安全功能的最简单的方法。即使其他线程愿意,也无法访问相同的单例对象。 迅捷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!
        }
    }
}

2
静态类型属性(即使同时跨多个线程访问,也可以保证仅延迟初始化一次)的优点是什么?
Martin R

-1

我要求单身人士允许继承,而这些解决方案均未允许继承。所以我想出了这个:

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的真实,相同的情况下被使用。

第一种肮脏方法的问题在于,我不能保证子类会实现,dispatch_once_t并确保sharedInstanceVar每个类仅对其进行一次修改。

我将尝试进一步完善它,但是很有趣的是,看看是否有人对此有强烈的想法(除了它冗长且需要手动更新之外)。



-2

我使用以下语法:

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之间运行,并具有以下优点:

  1. 提醒用户不要继承实现
  2. 防止创建其他实例
  3. 确保延迟创建和唯一实例化
  4. 通过允许以以下方式访问实例来缩短语法(避免使用()): Singleton.instance
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.