ChatGPT解决这个技术问题 Extra ChatGPT

在 Swift 中使用 dispatch_once 单例模型

我正在尝试制定一个适合在 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 初始化程序的只读属性。
我正是这个意思。不幸的是,我们仍然没有足够的内部信息。但是,恕我直言,@lazy 的任何实现都应该是线程安全的。
而且这种方式还有一个优点是不会将实现暴露给调用者的掠夺者。
您似乎也不能拥有@lazy 类变量。
当心!这种方法需要注意两点。首先,任何继承自它的类都必须覆盖 sharedInstance 属性。 Static.instance = TPScopeManager() 强制实例类型。如果您将 Static.instance = self() 之类的内容与必需的初始化程序一起使用,则会生成适当的类型类。即便如此,这是需要注意的重要一点,对于层次结构中的所有实例仅一次!要初始化的第一个类型是为所有实例设置的类型。我不认为objective-c 的行为是一样的。

A
Alexander Vasenin

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


“通过 let 实现线程安全”——这在任何地方都有说明吗?我在文档中找不到提及它。
@jtbandes 常量在我知道的所有语言中都是线程安全的。
@DaveWood 我假设您正在谈论最后一种方法。我会引用自己的话:“我会说不再需要使用这种方法,但我还是把它放在这里,因为我发现语法上的差异很有趣。”
是否也应将 init 声明为 private 以保证该对象的一个且只有一个实例将在应用程序的整个生命周期中存在?
在“类常量”方法中,我建议 (a) 将类声明为 final,这样您就不会对其进行子类化; (b) 将 init 方法声明为 private,这样您就不会意外地在某处实例化另一个实例。
6
6 revs, 2 users 99%

由于 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() {},以进一步强制执行此类不打算在外部实例化的事实。
所以 static struct var 初始化是惰性和线程安全的,如果那个 static struct var 是一个多吨字典,那么我们必须为每次访问手动同步/排队调用它,对吗?
如果我正确理解您的问题,字典和数组访问本质上不是线程安全的,因此您将需要使用某种形式的线程同步。
@DavidBerry 我应该如何在这个单例类中调用一个函数?我需要在第一次调用 myClass.sharedInstance 时调用一个函数。
J
Jack

对于 Swift 1.2 及更高版本:

class Singleton  {
   static let sharedInstance = Singleton()
}

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

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

至于使用 staticclass 的问题。即使 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。


仅阅读此答案的任何人:请记住将令牌设为静态,否则行为未定义。有关完整代码,请参阅 David 编辑的问题。
@nschum 否则,行为不是未定义的,它只是以明确定义的方式被破坏:块将始终执行。
@Michael:文档说明它是未定义的。因此,当前的行为是巧合。
说起来很奇怪。如果文档将其称为“未定义”,那只是意味着编写代码的人不会对其所做的事情做出任何承诺。它与知道变量是否为静态的代码无关。它只是意味着不能依赖当前(或明显的)行为。
您可能希望将 private init() {} 添加为 SingletonClass 的初始化程序。以防止从外部实例化。
S
Sahil

Swift 1.2 或更高版本现在支持类中的静态变量/常量。所以你可以只使用一个静态常量:

class MySingleton {

    static let sharedMySingleton = MySingleton()

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

N
Nilanshu Jaiswal

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

var tpScopeManagerSharedInstance = TPScopeManager()

这只是调用您的默认 init 或在 Swift 中默认为 dispatch_once 的任何 init 和全局变量。然后在你想获得参考的任何课程中,你只需这样做:

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

所以基本上你可以摆脱整个共享实例代码块。


为什么一个“var”和一个“let”?
也许可能是一个让,我只用一个 var 测试了它。
我喜欢这个答案,但是我需要从 Interface Builder 访问这个(Singleton)。关于如何从 IB 中访问此 tpScopeManagerSharedInstance 的任何想法?谢谢。-
这是我拥有单身人士的首选方式。它具有所有常用功能(线程安全和延迟实例化)并且它支持非常轻量级的语法:无需一直编写 TPScopeManager.sharedInstance.doIt(),只需将您的类命名为 TPScopeManagerClass,这个声明在类 public let TPScopeManager = TPScopeManagerClass() 旁边,使用时只需写 TPScopeManager.doIt()。很干净!
这里没有什么可以阻止创建 TPScopeManager 的其他实例,因此根据定义,它不是单例
N
Nilanshu Jaiswal

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

class MyClass {

    private static let _sharedInstance = MyClass()

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

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


赞成 LearnCocos2D 的评论 :) ,也赞成风格。
全局变量应通过类内的静态变量更改为类变量。
@malhal 当一个变量被标记为私有但在一个类之外时,它不是全局的 - 但仅限于它所在的文件。类内的静态工作几乎相同,但我已经更新了使用静态的答案正如您所建议的那样,如果您碰巧在文件中使用多个类,它可以更好地将变量分组到类中。
“Swift Singletons 在可可框架中作为类函数公开”......在 Swift 3 中没有。它们现在通常是 static 属性。
A
Adrian Macneil

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

class Singleton {
    static let sharedInstance = Singleton()
}

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

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

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


如何将静态 let 实例设置回 nil?
@ user1463853 - 你不能,通常也不应该。
A
Adam Smaka

斯威夫特 4+

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

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

这需要最后一课,你能解释更多区别吗,因为我对带有结构的单例的其他解决方案有疑问
那应该是私有覆盖init(){}
P
Peter Mortensen

查看 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 吗?
@Sam 是的。请参阅 Files and Initialization 上的 Apple 博客条目,其中清楚地表明结构和枚举的全局和静态成员都受益于此 dispatch_once 功能。
o
onmyway133

简单来说,

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

您可能想阅读Files and Initialization

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


M
Michael

如果您计划在 Objective-C 中使用 Swift 单例类,此设置将使编译器生成适当的类似 Objective-C 的标头:

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

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

[ImageStore sharedStore];

这只是我的简单实现。


这个其实更简洁&比其他示例正确,因为它的实现方式与其他 Swift 单例相同。即:作为类函数,如 NSFileManager.defaultManager(),但仍使用 Swift 的惰性线程安全静态成员机制。
如今,Cocoa 通常将这些实现为静态属性,而不是类函数。
我知道这一点,我的评论已经超过 2 年了。感谢提及。
P
Peter Mortensen

第一个解决方案

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

K
Kemal Can Kaynak
final class MySingleton {
     private init() {}
     static let shared = MySingleton()
}

然后调用它;

let shared = MySingleton.shared

做得好,不仅将 init 标记为 private,而且将 sharedMyModel 标记为 final!为了未来的读者,在 Swift 3 中,我们可能倾向于将 sharedMyModel 重命名为简单的 shared
这是唯一正确的答案,除了对 super.init 的覆盖和调用是错误的,甚至不会编译。
P
Peter Mortensen

利用:

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 除了没有全局变量。 :)
@hpique 不,就像我之前的尝试之一。查看编辑历史。
P
Peter Mortensen

Swift 1.2 以上的最佳方法是单行单例,如 -

class Shared: NSObject {

    static let sharedInstance = Shared()

    private override init() { }
}

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


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

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

N
Nilanshu Jaiswal

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

enum SharedTPScopeManager: TPScopeManager {
    case Singleton
}

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

仅供参考,这里是 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()
    }
}

V
Vicky Prajapati

在 swift 中,您可以通过以下方式创建一个单例类:

class AppSingleton: NSObject {

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

    override init() {
        super.init()
    }
}

D
Dark Matter

我更喜欢这种实现:

class APIClient {

}

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

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

u
user2737730

我在 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

E
Ely

唯一正确的方法如下。

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()
如果你不想做任何额外的设置,那么你说的是对的。
N
Nilanshu Jaiswal

在看到 David 的实现之后,似乎没有必要使用单例类函数 instanceMethod,因为 letsharedInstance 类方法做的事情几乎相同。您需要做的就是将其声明为全局常量,就是这样。

let gScopeManagerSharedInstance = ScopeManager()

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

正如我在评论中所说,这样做的唯一原因是在未来的某个时候,您可以移动/隐藏全局变量并获得更多类似单例的行为。那时,如果一切都使用一致的模式,您可以只更改单例类本身,而无需更改用法。
D
DD.amor
   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!
}

正如这里详细讨论的那样,在 swift 中没有必要将初始化包装在 dispatch_once 中,因为静态变量初始化是惰性的并且通过 dispatch_once 自动保护 Apple 实际上出于这个原因建议使用静态而不是 dispatch_once。
T
Tim

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

M
Mojtaba Hosseini

斯威夫特 5.2

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

static let shared = Self()

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

class SomeTypeWithASingletonInstance {
   static let shared = Self()
}

A
Abhishek Biswas

这是具有线程安全功能的最简单的一种。没有其他线程可以访问相同的单例对象,即使他们想要。斯威夫特 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!
        }
    }
}

与静态类型属性相比有什么优势(即使同时跨多个线程访问,也保证只延迟初始化一次)?
N
Nilanshu Jaiswal

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

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

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


P
Peter Mortensen

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

let TEST = Test()

class Test {

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

此处已建议 private initstackoverflow.com/a/28436202/1187415
N
Nilanshu Jaiswal

我使用以下语法:

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 访问来缩短语法(避免 ())


关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅