Использование модели singleton dispatch_once в Swift

Я пытаюсь разработать подходящую модель синглтон для использования в Swift. До сих пор я смог получить не-поточную безопасную модель, которая работает как:

class var sharedInstance:TPScopeManager { get { struct Static { static var instance : TPScopeManager? = nil } if !Static.instance { Static.instance = TPScopeManager() } return Static.instance! } } 

Объединение экземпляра singleton в Static struct должно позволить одному экземпляру, который не сталкивается с экземплярами singleton без сложных схем именования, и должен сделать вещи довольно конфиденциальными. Очевидно, что эта модель не является streamобезопасной, поэтому я попытался добавить 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 .

    tl; dr: Используйте подход констант classа, если вы используете Swift 1.2 или выше и вложенный структурный подход, если вам нужно поддерживать более ранние версии.

    Из моего опыта работы с Swift существует три подхода к реализации шаблона Singleton, который поддерживает ленивую инициализацию и безопасность streamов.

    Постоянная classа

     class Singleton { static let sharedInstance = Singleton() } 

    Этот подход поддерживает ленивую инициализацию, потому что Swift лениво инициализирует константы classа (и переменные) и является streamобезопасным по определению let . Теперь это официально рекомендованный способ создания экземпляра синглета.

    Константы classа были введены в Swift 1.2. Если вам нужна поддержка более ранней версии Swift, используйте подход сложенной структуры ниже или глобальную константу.

    Вложенная структура

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

    Здесь мы используем статическую константу вложенной структуры как константу classа. Это обходное решение для отсутствия статических констант classа в 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 } } 

    Это использует автоматическую ленивую, streamобезопасную инициализацию статических структурных элементов, безопасно скрывает фактическую реализацию от потребителя, сохраняет все компактность и разметку для удобочитаемости и исключает видимую глобальную переменную.

    Apple пояснила, что ленивый инициализатор является streamобезопасным, поэтому нет необходимости в dispatch_once или подобной защите

    Ленивый инициализатор для глобальной переменной (также для статических членов структур и перечислений) запускается при первом обращении к глобальному доступу и запускается как dispatch_once, чтобы убедиться, что инициализация является атомарной. Это позволяет использовать class dispatch_once в своем коде: просто объявите глобальную переменную с инициализатором и отметьте ее закрытой.

    Отсюда

    Для Swift 1.2 и выше:

     class Singleton { static let sharedInstance = Singleton() } 

    С доказательством правильности (все кредиты здесь ), сейчас почти нет причин использовать какой-либо из предыдущих методов для одиночных игроков.

    Обновление : теперь это официальный способ определить синглтоны, как описано в официальных документах !

    Что касается проблем использования static class vs. static должен использоваться только тогда, когда становятся доступными переменные class . Синглтоны не предназначены для подclassа, поскольку это приведет к нескольким экземплярам базового синглтона. Использование static обеспечивает это красивым, Swifty способом.

    Для Swift 1.0 и 1.1:

    С недавними изменениями в Swift, в основном новыми методами контроля доступа, я теперь склоняюсь к более чистому способу использования глобальной переменной для одиночных игр.

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

    Как упоминалось в статье Swift в блоге:

    Ленивый инициализатор для глобальной переменной (также для статических членов структур и перечислений) запускается при первом обращении к глобальному доступу и запускается как dispatch_once, чтобы убедиться, что инициализация является атомарной. Это позволяет использовать class dispatch_once в своем коде: просто объявите глобальную переменную с инициализатором и отметьте ее закрытой.

    Этот способ создания синглтона является streamобезопасным, быстрым, ленивым, а также мостом к ObjC бесплатно.

    Swift 1.2 или более поздняя версия поддерживает статические переменные / константы в classах. Таким образом, вы можете просто использовать статическую константу:

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

    Есть лучший способ сделать это. Вы можете объявить глобальную переменную в своем classе над объявлением classа следующим образом

     var tpScopeManagerSharedInstance = TPScopeManager() 

    Это просто вызывает инициализацию по умолчанию, или какие бы ни были init и глобальные переменные dispatch_once по умолчанию в Swift. Затем, в каком бы classе вы хотели получить ссылку, вы просто делаете это:

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

    Таким образом, вы можете избавиться от всего блока кода общего экземпляра.

    Swift-синглтоны отображаются в рамках Cocoa как функции classа, например NSFileManager.defaultManager() , NSNotificationCenter.defaultCenter() , поэтому я чувствую, что более разумно рассматривать функцию classа, чтобы отражать это поведение, а не переменную classа, как некоторые другие решения использование, например

     class MyClass { private static let _sharedInstance = MyClass() class func sharedInstance() -> MyClass { return _sharedInstance } } 

    MyClass.sharedInstance() одноэлемент через MyClass.sharedInstance() .

    Swift 4+

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

    В документации Apple многократно повторялось, что самый простой способ сделать это в Swift – это свойство статического типа:

     class Singleton { static let sharedInstance = Singleton() } 

    Однако, если вы ищете способ выполнить дополнительную настройку за пределами простого вызова конструктора, секрет заключается в том, чтобы использовать немедленно вызываемое закрытие:

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

    Это гарантировано для streamобезопасности и лениво инициализируется только один раз.

    Глядя на образец кода Apple, я наткнулся на этот шаблон. Я не уверен, как Swift имеет дело со статикой, но в C # это будет streamобезопасным. Я включаю как свойство, так и метод для взаимодействия Objective-C.

     struct StaticRank { static let shared = RankMapping() } class func sharedInstance() -> RankMapping { return StaticRank.shared } class var shared:RankMapping { return StaticRank.shared } 

    Если вы планируете использовать свой class Swift singleton в Objective-C, эта настройка заставит компилятор генерировать соответствующие заголовки Objective-C-like:

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

    Затем в classе Objective-C вы можете назвать свой синглтон так, как вы делали это в дни до Swift:

     [ImageStore sharedStore]; 

    Это просто моя простая реализация.

    Первое решение

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

    Использование:

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

    Вкратце,

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

    Возможно, вы захотите ознакомиться с файлами и инициализацией

    Ленивый инициализатор для глобальной переменной (также для статических членов структур и перечислений) запускается при первом обращении к глобальному доступу и запускается как dispatch_once чтобы убедиться, что инициализация является атомарной.

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

    Затем назовите его;

     let shared = MySingleton.shared 

    Я бы предложил Enum, как вы бы использовали в Java, например:

     enum SharedTPScopeManager: TPScopeManager { case Singleton } 

    Для справки, здесь приведен пример реализации Singleton реализации вложенного Struct 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 } 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 выше 1.2 является однострочный синглтон,

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

    Чтобы узнать более подробно об этом подходе, вы можете посетить эту ссылку .

    Я предпочитаю эту реализацию:

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

    Мой способ внедрения в 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 

    Из Apple Docs (Swift 3.0.1),

    Вы можете просто использовать свойство статического типа, которое гарантированно будет инициализироваться только один раз, даже если доступно одновременное обращение к нескольким streamам:

     class Singleton { static let sharedInstance = Singleton() } 

    Если вам нужно выполнить дополнительную настройку после инициализации, вы можете назначить результат вызова закрытия глобальной константе:

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

    После того, как вы увидели реализацию Дэвида, кажется, что нет необходимости использовать функцию singleton class instanceMethod, так как let делает почти то же самое, что и метод classа 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! } 

    Я просто натолкнулся на это, но я потребовал, чтобы мой синглтон разрешил наследование, и ни одно из этих решений на самом деле не разрешило его.

    Поэтому я придумал это:

     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 и используется тот же самый экземпляр.

    Проблема с этим первым грязным подходом заключается в том, что я не могу гарантировать, что подclassы будут реализовывать dispatch_once_t и убедиться, что sharedInstanceVar изменяется только один раз за class …

    Я попытаюсь уточнить это дальше, но было бы интересно узнать, есть ли у кого-то сильные чувства против этого (помимо того факта, что он многословен и требует его вручную обновить).

    Это самый простой вариант с поддержкой streamов. Ни один другой stream не может получить доступ к одному и тому же объекту singleton, даже если он этого захочет. Swift 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! } } } 

    Быстро реализовать синглтон в прошлом – это не что иное, как три способа: глобальные переменные, внутренние переменные и пути dispatch_once.

    Вот два хороших синглтона (обратите внимание: независимо от того, какой тип письма должен обратить внимание на метод privatisation init (). Поскольку в Swift все стандартные значения конструктора объекта являются общедоступными, их необходимо перезаписать. Init может быть превращен в закрытый , запретить другим объектам этого classа ‘()’ по умолчанию метод инициализации для создания объекта.)

    Способ 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 

    Это моя реализация. Это также мешает программисту создать новый экземпляр:

     let TEST = Test() class Test { private init() { // This is a private (!) constructor } } 
     private var sharedURLCacheForRequestsKey:Void? extension URLCache{ public static func sharedURLCacheForRequests()->URLCache{ var cache = objc_getAssociatedObject(OperationQueue.main, &sharedURLCacheForRequestsKey) if cache is URLCache { }else{ cache = URLCache(memoryCapacity: 0, diskCapacity: 1*1024*1024*1024, diskPath: "sharedURLCacheForRequestsKey") objc_setAssociatedObject(OperationQueue.main, &sharedURLCacheForRequestsKey, cache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } return cache as! URLCache }} 

    Я предпочитаю использовать следующий синтаксис как наиболее полный:

     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. Напоминает, что пользователь не выполняет подclass
    2. Предотвращает создание дополнительных экземпляров
    3. Обеспечивает ленивое создание и уникальное создание
    4. Сокращает синтаксис (avoids ()), позволяя обращаться к экземпляру как Singleton.instance
    Interesting Posts

    Объединение нескольких модулей DIMM в один канал памяти

    java.lang.NoClassDefFoundError: com / sun / mail / util / MailLogger для тестового примера JUnit для Java-почты

    Как добавить один месяц к текущей дате в Java?

    Будет ли обязательная политика обновления в Windows 10 включать проблемы безопасности?

    Android LocationServices.FusedLocationApi устарел

    Как я могу читать ввод с клавиатуры хоста при подключении через SSH?

    Netbeans – Ошибка: не удалось найти или загрузить основной class

    std :: sort не всегда вызывает std :: swap

    Анализ ответа ksoap2

    Как переименовать файлы с пробелами с помощью оболочки Linux?

    Как заставить Qt работать, когда основной stream занят?

    Последняя дата доступа не изменяется даже после чтения файла в Windows 7

    Ограничения на установку расширений или надстроек в Visual Studio 2010 Express

    Как предотвратить использование автозапуска с помощью Git?

    Пользовательский блок завершения для моего собственного метода

    Давайте будем гением компьютера.