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

549
03 июня '14 в 23:41
источник поделиться
32 ответов
  • 1
  • 2

tl; dr: используйте подход class constant, если вы используете 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 проект для модульных тестов.

691
10 июня '14 в 20:57
источник

Связанные вопросы


Похожие вопросы

Так как 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 в своем коде: просто объявите глобальную переменную с инициализатором и пометьте ее конфиденциальной.

От здесь

173
06 июня '14 в 4:58
источник

Для Swift 1.2 и выше:

class Singleton  {
   static let sharedInstance = Singleton()
}

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

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

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

Для Swift 1.0 и 1.1:

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

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

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

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

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

162
03 июня '14 в 23:55
источник

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

class MySingleton {

    static let sharedMySingleton = MySingleton()

    private init() {
        // ...
    }
}
46
10 февр. '15 в 19:03
источник

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

var tpScopeManagerSharedInstance = TPScopeManager()

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

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

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

33
05 июня '14 в 21:02
источник

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

class MyClass {

    private static let _sharedInstance = MyClass()

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

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

26
13 янв. '15 в 6:36
источник

Swift 4 +

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

final class Kraken: Singleton {
    static let sharedInstance = Kraken()
    private init() {}
}
16
09 сент. '16 в 15:51
источник

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

class Singleton {
    static let sharedInstance = Singleton()
}

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

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

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

16
18 сент. '15 в 10:31
источник

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

struct StaticRank {
    static let shared = RankMapping()
}

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

class var shared:RankMapping {
    return StaticRank.shared
}
8
05 июня '14 в 5:29
источник

Вкратце,

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

Вы можете прочитать файлы и инициализация

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

5
25 янв. '16 в 18:47
источник
final class MySingleton {
     private init() {}
     static let shared = MySingleton()
}

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

let shared = MySingleton.shared
4
17 нояб. '15 в 16:59
источник

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

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

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

[ImageStore sharedStore];

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

4
15 июня '14 в 19:28
источник

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

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
24 авг. '14 в 20:33
источник

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

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)")
4
06 июня '14 в 11:01
источник

Лучший подход в Swift выше 1.2 - однострочный однострочный, as -

class Shared: NSObject {

    static let sharedInstance = Shared()

    private override init() { }
}

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

4
23 апр. '16 в 20:22
источник

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

enum SharedTPScopeManager: TPScopeManager {
  case Singleton
}
3
20 июня '14 в 3:56
источник

От Apple Документы (Swift 3.0.1),

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

class Singleton {
    static let sharedInstance = Singleton()
}

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

class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
        // setup code
        return instance
    }()
}
3
27 сент. '16 в 18:21
источник

Просто для справки, вот пример реализации Singleton реализации Jack Wu/hpique Nested Struct. Реализация также показывает, как может работать архивирование, а также некоторые сопутствующие функции. Я не мог найти этот полный пример, так что, надеюсь, это поможет кому-то!

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()
    }
}
2
26 июня '14 в 23:04
источник

Единственный правильный подход ниже

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, чтобы другие классы не могли создать экземпляр.
  • последний класс, так как вы не хотите, чтобы другие классы наследовали класс Singleton
2
17 окт. '18 в 19:45
источник

Мой способ реализации в 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
24 июля '14 в 12:57
источник

После просмотра реализации David кажется, что нет необходимости использовать функцию singleton class instanceMethod, поскольку let делает почти то же самое, что и метод класса sharedInstance. Все, что вам нужно сделать, это объявить его глобальной константой, и это будет так.

let gScopeManagerSharedInstance = ScopeManager()

class ScopeManager {
 // No need for a class method to return the shared instance. Use the gScopeManagerSharedInstance directly. 
}
1
07 июня '14 в 6:50
источник

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

class APIClient {

}

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

extension APIClient {
    class func sharedClient() -> APIClient {
        return sharedAPIClient
    }
}
1
12 июня '14 в 2:16
источник

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

Вот два хороших синглтона (обратите внимание: независимо от того, какой тип письма должен обратить внимание на метод privatisation init(). Поскольку в Swift все стандартные конструкторы объектов являются общедоступными, их необходимо перезаписать. 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
0
03 янв. '18 в 10:19
источник

Вышеупомянутое решение работает, но ни одно из этих решений не учитывает параметризованные случаи конструктора. Также эти синглы будут инициализированы в начале, но в некоторых случаях требуется отложенная инициализация, поэтому подход должен отличаться в этом случае. Статическая переменная sharedInstance не должна предоставляться напрямую из класса, поскольку ее значение будет использоваться с использованием метода getInstance, поскольку она может содержать код инициализации объекта в первый раз и может использоваться для отложенной инициализации.

0
19 дек. '18 в 10:22
источник

В swift вы можете создать одноэлементный класс следующим образом:

class AppSingleton: NSObject {

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

    override init() {
        super.init()
    }
}
0
26 апр. '19 в 9:26
источник
   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
27 янв. '15 в 10:44
источник

Это самый простой вариант с поддержкой потоков. Ни один другой поток не может получить доступ к одному и тому же объекту 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!
        }
    }
}
-1
12 окт. '17 в 15:32
источник

используйте статическую переменную и частный инициализатор для создания одноэлементного класса.

class MySingletonClass {

    static let sharedSingleton = MySingletonClass()

    private init() {}
}
-1
19 сент. '18 в 10:59
источник

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

Итак, я придумал это:

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 изменяется только один раз за класс...

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

-1
04 февр. '16 в 3:00
источник

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

let TEST = Test()

class Test {

    private init() {
        // This is a private (!) constructor
    }
}
-2
28 дек. '16 в 13:04
источник
  • 1
  • 2

Посмотрите другие вопросы по меткам или Задайте вопрос