【问题标题】:Using a dispatch_once singleton model in Swift在 Swift 中使用 dispatch_once 单例模型
【发布时间】:2014-07-24 08:13:28
【问题描述】:

我正在尝试制定一个适合在 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 的行为是一样的。

标签: swift singleton dispatch


【解决方案1】:

对于 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 中提到的:

全局变量的惰性初始化器(也适用于 structs 和 enums)在第一次访问 global 时运行,并且 作为 dispatch_once 启动以确保初始化是 原子。这为在代码中使用 dispatch_once 提供了一种很酷的方式: 只需使用初始化程序声明一个全局变量并标记它 私人的。

这种创建单例的方式是线程安全的、快速的、惰性的,并且还可以免费桥接到 ObjC。

【讨论】:

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

【讨论】:

  • 我很确定只要使用这个默认的静态语法就可以完成所有烦人的工作。
  • 不幸的是,静态只在结构内部起作用,所以这就是这种模式的原因。
  • 我的意图是我们不必使用dispatch_once 的东西。我赌的是你的风格。 :)
  • 类声明中的class 不等同于结构声明中的static
  • @Sam 是的。请参阅 Files and Initialization 上的 Apple 博客条目,其中清楚地表明结构和枚举的全局和静态成员都受益于 dispatch_once 功能。
【解决方案3】:

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

var tpScopeManagerSharedInstance = TPScopeManager()

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

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

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

【讨论】:

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

来自here

【讨论】:

  • 确认:全局变量具有惰性、线程安全的初始化,但类变量没有。对吗?
  • 我要补充一点,一个好的做法是将初始化程序声明为私有:private init() {},以进一步强制执行此类不打算在外部实例化的事实。
  • 所以静态 struct var 初始化是惰性的并且是线程安全的,如果该静态 struct var 是多吨字典,那么我们必须为每次访问手动同步/排队调用它,对吗?跨度>
  • 如果我正确理解您的问题,字典和数组访问本质上不是线程安全的,因此您需要使用某种形式的线程同步。
  • @DavidBerry 我应该如何在这个单例类中调用函数?我需要在第一次调用 myClass.sharedInstance 时调用一个函数。
【解决方案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)")

【讨论】:

  • 这与我在获得当前答案的途中所经历的答案之一完全相同。由于全局变量是惰性和线程安全的,因此没有理由增加复杂性。
  • @David 除了没有全局变量。 :)
  • @hpique 不,就像我之前的尝试之一。查看编辑历史记录。
【解决方案6】:

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

let gScopeManagerSharedInstance = ScopeManager()

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

【讨论】:

  • 正如我在我的 cmets 中所说的,这样做的唯一原因是,在将来的某个时候,您可以移动/隐藏全局变量并获得更多类似单例的行为。那时,如果一切都使用一致的模式,您可以只更改单例类本身,而无需更改用法。
【解决方案7】:

tl;dr:如果您使用的是 Swift 1.2 或更高版本,请使用 class constant 方法,如果您需要支持早期版本,请使用 nested struct 方法。

根据我使用 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,这样您就不会意外地在某处实例化另一个实例。
【解决方案8】:

我更喜欢这种实现方式:

class APIClient {

}

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

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

【讨论】:

    【解决方案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];
    

    这只是我的简单实现。

    【讨论】:

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

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

    enum SharedTPScopeManager: TPScopeManager {
        case Singleton
    }
    

    【讨论】:

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

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

    【讨论】:

      【解决方案12】:

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

      【讨论】:

        【解决方案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()        
        }
        

        【讨论】:

          【解决方案14】:

          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 在 cocoa 框架中作为类函数公开”......不在 Swift 3 中。它们现在通常是 static 属性。
          【解决方案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!
          }
          

          【讨论】:

          • 正如在这里详细讨论的那样,在 swift 中没有必要在 dispatch_once 中包装初始化,因为静态变量初始化是惰性的并且通过 dispatch_once 自动保护苹果实际上建议使用静态而不是 dispatch_once出于这个原因。
          【解决方案16】:

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

          class MySingleton {
          
              static let sharedMySingleton = MySingleton()
          
              private init() {
                  // ...
              }
          }
          

          【讨论】:

            【解决方案17】:

            根据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 - 你不能,通常也不应该。
            【解决方案18】:
            final class MySingleton {
                 private init() {}
                 static let shared = MySingleton()
            }
            

            然后调用它;

            let shared = MySingleton.shared
            

            【讨论】:

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

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

            let TEST = Test()
            
            class Test {
            
                private init() {
                    // This is a private (!) constructor
                }
            }
            

            【讨论】:

            【解决方案20】:

            简而言之,

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

            您可能想阅读Files and Initialization

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

            【讨论】:

              【解决方案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 的实例
              • 首先执行SubSingleton.sharedInstance() 时,它将返回创建的SubSingleton 实例。
              • 如果以上都做了,那么SubSingleton.sharedInstance()Singleton为真,使用同一个实例。

              第一种肮脏的方法的问题是,我不能保证子类会实现 dispatch_once_t 并确保每个类只修改一次 sharedInstanceVar

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

              【讨论】:

                【解决方案22】:

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

                class Shared: NSObject {
                
                    static let sharedInstance = Shared()
                
                    private override init() { }
                }
                

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

                【讨论】:

                【解决方案23】:

                Swift 4+

                protocol Singleton: class {
                    static var sharedInstance: Self { get }
                }
                
                final class Kraken: Singleton {
                    static let sharedInstance = Kraken()
                    private init() {}
                }
                

                【讨论】:

                • 这需要最后一课,你能解释更多区别吗,因为我对带有结构的单例的其他解决方案有疑问
                • 应该是private override init() {}
                【解决方案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
                    }()
                }
                

                【讨论】:

                  【解决方案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!
                          }
                      }
                  }
                  

                  【讨论】:

                  • 与静态类型属性相比有什么优势(保证只延迟初始化一次,即使同时跨多个线程访问)?
                  【解决方案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 访问实例来缩短语法(避免 ())

                  【讨论】:

                    【解决方案27】:

                    以往Swift实现单例,无非就是三种方式:全局变量、内部变量和dispatch_once方式。

                    这里有两个很好的singleton。(注意:无论哪种写法都要注意私有化的init()方法。因为在Swift中,所有对象的构造函数默认都是public的,需要重写init即可变成私有的,防止这个类的其他对象'()'默认初始化方法创建对象。)

                    方法一:

                    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
                    

                    【讨论】:

                      【解决方案28】:

                      下面是唯一正确的方法。

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

                      访问

                      let signleton = Singleton.sharedInstance
                      

                      原因:

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

                      【讨论】:

                      • 你可以直接使用static let sharedInstance = Singleton() 为什么使用闭包初始化
                      • 如果你不想做任何额外的设置,那么你说的是对的。
                      【解决方案29】:

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

                      class AppSingleton: NSObject {
                      
                          //Shared instance of class
                          static let sharedInstance = AppSingleton()
                      
                          override init() {
                              super.init()
                          }
                      }
                      

                      【讨论】:

                        【解决方案30】:

                        斯威夫特 5.2

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

                        static let shared = Self()
                        

                        并且应该在一个类型里面,比如:

                        class SomeTypeWithASingletonInstance {
                           static let shared = Self()
                        }
                        

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 2011-08-08
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2012-02-03
                          • 2012-02-25
                          相关资源
                          最近更新 更多