在 Swift 中使用dispatch_once单例模型

Using a dispatch_once singleton model in Swift

提问人:David Berry 提问时间:6/4/2014 最后编辑:Nilanshu JaiswalDavid Berry 更新时间:8/3/2020 访问量:138077

问:

我正在尝试制定一个合适的单例模型,以便在 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 单例 调度

评论

3赞 Sulthan 6/4/2014
我将删除所有这些静态代码,并使用带有 @lazy 初始值设定项的只读属性。
1赞 Sulthan 6/4/2014
这就是我的意思。不幸的是,我们仍然没有足够的关于内部的信息。但是,恕我直言,任何实现都应该是线程安全的。@lazy
1赞 David Berry 6/4/2014
这种方式还具有不会将实现暴露给调用方的掠夺的优点。
1赞 David Berry 6/4/2014
似乎也不能有@lazy类变量。
0赞 sean woodward 6/5/2014
小心!使用这种方法需要注意两点。首先,从此继承的任何类都必须重写 sharedInstance 属性。 强制使用实例类型。如果将类似的东西与必需的初始值设定项一起使用,则将生成相应的类型类。即便如此,这是需要注意的重要一点,层次结构中的所有实例只能使用一次!要初始化的第一个类型是为所有实例设置的类型。我不认为objective-c的行为是一样的。Static.instance = TPScopeManager()Static.instance = self()

答:

164赞 Jack 6/4/2014 #1

对于 Swift 1.2 及更高版本:

class Singleton  {
   static let sharedInstance = Singleton()
}

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

更新:这是现在定义单例的官方方式,如官方文档中所述!

至于对使用与. 即使变量可用,也应该使用。单例不应被子类化,因为这将导致基本单例的多个实例。使用以一种美丽、迅捷的方式强制执行这一点。staticclassstaticclassstatic

对于 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。

评论

2赞 nschum 6/5/2014
任何只阅读此答案的人:请记住将令牌设置为静态,否则行为是未定义的。有关完整代码,请参阅 David 编辑的问题。
0赞 Michael 6/10/2014
@nschum否则,行为不是未定义的,它只是以一种明确定义的方式被破坏:块将始终执行。
0赞 nschum 6/11/2014
@Michael:文档指出它是未定义的。因此,当前的行为是巧合。
1赞 nschum 6/11/2014
这话说起来很奇怪。如果文档称其为“未定义”,那只是意味着编写代码的人不会对它的功能做出任何承诺。它与知道变量是否静态的代码无关。它只是意味着当前(或明显的)行为不能被依赖。
6赞 rintaro 9/24/2014
您可能希望添加为 的初始值设定项。以防止从外部实例化。private init() {}SingletonClass
8赞 user2485100 6/5/2014 #2

查看 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
}

评论

0赞 eonil 6/5/2014
我很确定仅使用此默认静态语法即可完成所有烦人的工作。
0赞 user2485100 6/5/2014
不幸的是,静态只在结构体内部起作用,所以这就是这种模式的原因。
0赞 eonil 6/5/2014
我的意图是,我们不必使用东西。我打赌你的风格。:)dispatch_once
0赞 Russell Borogove 6/6/2014
类声明中不是等同于结构声明中的吗?classstatic
0赞 Rob 2/14/2015
@Sam 是的。请参阅有关文件和初始化的 Apple 博客文章,其中明确指出结构和枚举的全局成员和静态成员都受益于此功能。dispatch_once
35赞 Kris Gellci 6/6/2014 #3

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

var tpScopeManagerSharedInstance = TPScopeManager()

这只调用你的默认 init 或 Swift 中默认的 init 和全局变量。然后,在你想获得引用的类中,你只需这样做:dispatch_once

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

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

评论

3赞 Stephan 6/7/2014
为什么是“var”和“let”?
1赞 Kris Gellci 6/7/2014
也许可能是一个让,我只用 var 测试了它。
0赞 Luis Palacios 7/20/2014
我喜欢这个答案,但是我需要从Interface Builder访问这个(Singleton)。关于如何从 IB 中访问此 tpScopeManagerSharedInstance 的任何想法?谢谢。-
0赞 Alex 3/7/2015
这是我首选的单例方式。它具有所有常用功能(线程安全和延迟实例化),并且支持非常轻量级的语法:无需一直编写,只需命名您的类,在类旁边有此声明,并且在使用时只需编写。很干净!TPScopeManager.sharedInstance.doIt()TPScopeManagerClasspublic let TPScopeManager = TPScopeManagerClass()TPScopeManager.doIt()
0赞 Caleb 3/15/2016
这里没有什么可以阻止创建其他实例的,因此根据定义,它不是单例TPScopeManager
176赞 6 revs, 2 users 99%David Berry #4

由于 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提供了一种很酷的方式:只需使用初始值设定项声明一个全局变量并将其标记为私有。

这里开始

评论

1赞 Bill 6/6/2014
确认:全局变量具有延迟的线程安全初始化,但类变量没有。右?
14赞 Pascal Bourque 9/27/2014
我想补充一点,一个好的做法是将初始值设定项声明为 private: ,以进一步强化此类不打算在外部实例化的事实。private init() {}
1赞 12/31/2014
所以静态结构 var 初始化是惰性和线程安全的,如果该静态结构 var 是多吨的字典怎么办,那么我们必须手动同步/排队每次访问对它的调用,对吧?
0赞 David Berry 1/1/2015
如果我正确理解您的问题,字典和数组访问本质上不是线程安全的,因此您需要使用某种形式的线程同步。
0赞 Bhushan B 9/8/2015
@DavidBerry 我应该如何调用这个单例类中的函数?我需要在第一次调用myClass.sharedInstance时调用一个函数。
4赞 Kingiol 6/6/2014 #5

用:

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)")

评论

0赞 David Berry 6/6/2014
这与我在获得当前答案的路上所经历的答案之一完全相同。由于全局变量的初始化既是惰性的,也是线程安全的,因此没有理由增加复杂性。
0赞 hpique 6/10/2014
@David除了没有全局变量。:)
0赞 David Berry 6/10/2014
@hpique不,就像我之前的一次尝试一样。查看编辑历史记录。
1赞 Essa A. Haddad 6/7/2014 #6

在看到 David 的实现之后,似乎没有必要使用单例类函数,因为它与类方法几乎相同。您需要做的就是将其声明为全局常量,仅此而已。instanceMethodletsharedInstance

let gScopeManagerSharedInstance = ScopeManager()

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

评论

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

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

评论

16赞 jtbandes 6/19/2014
“凭借 let 确保线程安全”——这在任何地方都有说明吗?我在文档中找不到提及它的内容。
4赞 hpique 6/19/2014
@jtbandes常量在我所知道的所有语言中都是线程安全的。
2赞 hpique 6/22/2014
@DaveWood 我假设你说的是最后一种方法。我会引用自己的话:“我想说不再需要使用这种方法,但无论如何我还是把它放在这里,因为我发现语法上的差异很有趣。
5赞 Andrew 5/27/2015
是否还应声明以保证在应用的整个生命周期内仅存在一个对象实例?initprivate
6赞 Rob 9/22/2016
在“类常量”方法中,我建议 (a) 声明类,这样你就不会对它进行子类化;(b) 声明方法,这样您就不会意外地在某处实例化另一个实例。finalinitprivate
1赞 Dark Matter 6/12/2014 #8

我更喜欢这种实现:

class APIClient {

}

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

extension APIClient {
    class func sharedClient() -> APIClient {
        return sharedAPIClient
    }
}
4赞 Michael 6/16/2014 #9

如果你打算在 Objective-C 中使用 Swift 单例类,此设置将让编译器生成适当的类似 Objective-C 的头文件:

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

然后,在 Objective-C 类中,你可以像在 Swift 之前那样调用你的单例:

[ImageStore sharedStore];

这只是我的简单实现。

评论

0赞 Leslie Godwin 11/3/2015
这实际上比其他例子更简洁、更正确,因为它的实现方式与其他 Swift 单例相同。即:作为类函数,但仍然使用 Swift 的惰性线程安全静态成员机制。NSFileManager.defaultManager()
0赞 Rob 9/22/2016
Cocoa 通常将这些实现为静态属性,而不是类函数。
0赞 Michael 9/24/2016
我知道这一点,我的评论已经有 2 年多了。谢谢你的提及。
3赞 Howard Lovatt 6/20/2014 #10

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

enum SharedTPScopeManager: TPScopeManager {
    case Singleton
}

评论

0赞 Bryan Chen 7/1/2014
IMO,这是实现 Singleton 的唯一正确的 Swift 方法。其他答案是 ObjC/C/C++ 方式
0赞 Kenny Winker 7/6/2014
你能详细说明一下这个答案吗?我不清楚 Singleton 是从这个片段中实例化的
0赞 Howard Lovatt 7/7/2014
@KennyWinker我没有 Apple 开发人员登录,因此没有 swift,因此我无法回答初始化何时发生。在 Java 中,它是首次使用。也许您可以尝试在初始化时打印它,看看打印是在启动时还是访问后发生。这将取决于编译器如何实现枚举。
0赞 Howard Lovatt 8/4/2014
@KennyWinkler:苹果刚刚澄清了这是如何工作的,请参阅 developer.apple.com/swift/blog/?id=7。在其中,他们说“在第一次引用时运行全局初始化器,类似于 Java”,尤其是。他们还说,在幕后,他们正在使用“dispatch_once来确保初始化是原子的”。因此,除非你有一些花哨的初始化工作要做,否则几乎可以肯定的是,enum 是要走的路,那么私有静态 let 就是解决方案。
2赞 SchoonSauce 6/27/2014 #11

仅供参考,这里是 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()
    }
}
1赞 user2737730 7/24/2014 #12

我在 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
4赞 Nicolas Manzini 8/25/2014 #13

第一个解决方案

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()        
}
28赞 Ryan 1/13/2015 #14

Swift 单例在 Cocoa 框架中作为类函数公开,例如 、.因此,作为类函数来反映这种行为比作为类变量作为其他一些解决方案更有意义。例如:NSFileManager.defaultManager()NSNotificationCenter.defaultCenter()

class MyClass {

    private static let _sharedInstance = MyClass()

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

通过 检索单例。MyClass.sharedInstance()

评论

1赞 x4h1d 10/14/2015
对 LearnCocos2D 的评论投了赞成票 :) ,也为风格投了赞成票。
2赞 malhal 12/1/2015
全局变量应通过类内部的 static 更改为类变量。
2赞 Ryan 12/2/2015
@malhal,当变量被标记为私有但在类之外时,它不是全局变量,而是仅限定于它所在的文件。类内的静态工作方式几乎相同,但是我已经更新了答案以按照您的建议使用静态,因为如果您碰巧在文件中使用多个类,它可以更好地将变量分组到类中。
1赞 Rob 9/22/2016
“Swift Singleton 在 cocoa 框架中作为类函数公开”......在 Swift 3 中没有。它们现在通常是属性。static
0赞 DD.amor 1/27/2015 #15
   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!
}

评论

0赞 David Berry 1/28/2015
正如这里已经详细讨论过的,在 swift 中没有必要包装初始化,因为静态变量初始化是延迟的,并且通过 Apple 自动保护,因此实际上建议使用静态而不是dispatch_once。dispatch_oncedispatch_once
47赞 Florian 2/11/2015 #16

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

class MySingleton {

    static let sharedMySingleton = MySingleton()

    private init() {
        // ...
    }
}
17赞 Adrian Macneil 9/18/2015 #17

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

class Singleton {
    static let sharedInstance = Singleton()
}

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

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

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

评论

0赞 gpichler 11/20/2015
如何将静态 let 实例设置回 NIL?
1赞 Rob 9/22/2016
@user1463853 - 你不能,而且通常不应该。
4赞 Kemal Can Kaynak 11/17/2015 #18
final class MySingleton {
     private init() {}
     static let shared = MySingleton()
}

然后打电话给它;

let shared = MySingleton.shared

评论

0赞 Rob 11/11/2016
做得很好,不仅标记为 ,而且使 as !为了未来的读者,在 Swift 3 中,我们可能倾向于重命名为 .initprivatesharedMyModelfinalsharedMyModelshared
0赞 Michael Morris 4/16/2017
这是唯一正确的答案,除了覆盖和对 super.init 的调用是错误的,甚至不会编译。
-2赞 Yedidya Reiss 12/28/2015 #19

这是我的实现。它还会阻止程序员创建新实例:

let TEST = Test()

class Test {

    private init() {
        // This is a private (!) constructor
    }
}

评论

0赞 Martin R 12/28/2015
private init这里已经建议了:stackoverflow.com/a/28436202/1187415
5赞 onmyway133 1/25/2016 #20

简而言之,

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

您可能想阅读文件和初始化

全局变量的惰性初始值设定项(也适用于 structs 和 enums) 在第一次访问 global 时运行,并且 启动,以确保初始化是 原子。dispatch_once

-1赞 RicardoDuarte 2/4/2016 #21

我要求我的单例允许继承,但这些解决方案实际上都不允许继承。所以我想出了这个:

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_tsharedInstanceVar

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

4赞 CodeCracker 4/24/2016 #22

在 1.2 以上的 Swift 中,最好的方法是单行单例,因为 -

class Shared: NSObject {

    static let sharedInstance = Shared()

    private override init() { }
}

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

评论

0赞 Martin R 4/24/2016
为什么是子类?除此之外,这似乎与 stackoverflow.com/a/28436202/1187415 基本相同。NSObject
16赞 Adam Smaka 9/9/2016 #23

斯威夫特 4+

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

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

评论

2赞 Raheel Sadiq 9/25/2016
这需要最后一堂课,你能解释更多区别吗,因为我对带有结构的单例的其他解决方案有问题
0赞 NSRover 12/14/2016
那应该是私有的 override init() {}
3赞 Rukshan 9/27/2016 #24

从 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
    }()
}
-1赞 Abhishek Biswas 10/12/2017 #25

这是具有线程安全功能的最简单的方法。任何其他线程都无法访问相同的单一实例对象,即使它们需要。斯威夫特 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 10/12/2017
与静态类型属性(保证仅延迟初始化一次,即使同时跨多个线程访问)相比,它的优势是什么?
-2赞 Gonzalo Durañona 10/21/2017 #26

我使用以下语法:

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
0赞 Tim 1/3/2018 #27

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
1赞 Paresh Masani 10/18/2018 #28

唯一正确的方法如下。

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

    private init() {}
}

访问

let signleton = Singleton.sharedInstance

原因:

  • statictype 属性保证只延迟初始化一次,即使同时跨多个线程访问也是如此,因此无需使用dispatch_once
  • 私有化方法,使实例不能由其他类创建。init
  • final类,因为您不希望其他类继承 Singleton 类。

评论

0赞 abhimuralidharan 3/19/2019
为什么使用闭包初始化,而你可以直接使用static let sharedInstance = Singleton()
1赞 Paresh Masani 3/20/2019
如果您不想进行任何额外的设置,那么您所说的是正确的。
2赞 Vicky Prajapati 4/26/2019 #29

在 swift 中,您可以按以下方式创建单例类:

class AppSingleton: NSObject {

    //Shared instance of class
    static let sharedInstance = AppSingleton()

    override init() {
        super.init()
    }
}
0赞 Mojtaba Hosseini 8/3/2020 #30

斯威夫特 5.2

您可以使用 指向类型。所以:Self

static let shared = Self()

并且应该在一个类型中,例如:

class SomeTypeWithASingletonInstance {
   static let shared = Self()
}