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

515

Я пытаюсь разработать подходящую модель синглтон для использования в 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.

  • 3
    Я бы удалил весь этот статический код и использовал бы свойство readonly с инициализатором @lazy.
  • 1
    Это то, что я имел в виду. К сожалению, нам все еще не хватает информации о внутренних органах. Однако, IMHO, любая реализация @lazy должна быть поточно- @lazy .
Показать ещё 8 комментариев
Теги:
singleton
dispatch

31 ответ

710
Лучший ответ

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 проект для модульных тестов.

  • 12
    «Поток безопасен благодаря let» - это было где-нибудь заявлено? Я не могу найти упоминание об этом в документации.
  • 4
    @jtbandes Константы являются потокобезопасными на всех языках, которые я знаю.
Показать ещё 20 комментариев
180

Так как 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
    Для подтверждения: глобальные переменные имеют ленивую, поточно-ориентированную инициализацию, а переменные класса - нет. Правильно?
  • 13
    Я бы добавил, что хорошей практикой было бы объявить инициализатор как private: private init() {} , чтобы еще больше усилить тот факт, что этот класс не предназначен для внешнего применения.
Показать ещё 4 комментария
152

Для 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 бесплатно.

  • 2
    Любой, кто читает только этот ответ: не забудьте сделать токен статичным, иначе поведение не определено. Смотрите отредактированный вопрос Дэвида для полного кода.
  • 0
    @nschum в противном случае, поведение не является неопределенным, оно просто четко определено: блок всегда будет выполняться.
Показать ещё 8 комментариев
45

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

class MySingleton {

    static let sharedMySingleton = MySingleton()

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

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

var tpScopeManagerSharedInstance = TPScopeManager()

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

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

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

  • 3
    Почему «вар» и много «пусть»?
  • 1
    Может быть, это может быть, я проверил это только с помощью var.
Показать ещё 4 комментария
26

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

class MyClass {

    private static let _sharedInstance = MyClass()

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

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

  • 1
    проголосовал за комментарий LearnCocos2D :), а также за стиль.
  • 1
    глобальная переменная должна быть изменена на переменную класса через статическое внутри класса.
Показать ещё 2 комментария
16

Swift 4 +

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

final class Kraken: Singleton {
    static let sharedInstance = Kraken()
    private init() {}
}
  • 2
    для этого нужен последний класс, можете ли вы объяснить больше разницы, потому что у меня есть проблема с другим решением синглтона со структурой
  • 0
    если это частное переопределение init () {}
15

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

class Singleton {
    static let sharedInstance = Singleton()
}

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

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

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

  • 0
    Как вы можете установить статический экземпляр let обратно на ноль?
  • 1
    @ user1463853 - Вы не можете, и, как правило, не должны.
9

Глядя на образец кода 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
}
  • 0
    Я уверен, что использование статического синтаксиса по умолчанию сделает все надоедливые задания.
  • 0
    к сожалению, статика работает только внутри структур, поэтому этот шаблон.
Показать ещё 4 комментария
5

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

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

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

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

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

[ImageStore sharedStore];

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

  • 0
    Это на самом деле более сжато и правильно, чем в другом примере, потому что оно реализовано так же, как и другие синглтоны Swift. то есть: как функции класса, такие как NSFileManager.defaultManager() , но все еще использует ленивые поточно- NSFileManager.defaultManager() механизмы статического члена Swift.
  • 0
    Какао обычно реализует их как статические свойства, а не как функции класса.
Показать ещё 1 комментарий
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
    Это точно так же, как один из ответов, которые я прошел на пути к текущему ответу. Поскольку глобальные переменные инициализируются как ленивыми, так и поточно-ориентированными, нет причин для дополнительной сложности.
  • 0
    @David Кроме глобальной переменной. :)
Показать ещё 1 комментарий
4

Вкратце,

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

Возможно, вы захотите прочитать Файлы и инициализация

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

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

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

let shared = MySingleton.shared
  • 0
    Хорошо сделано не только для того, чтобы пометить init как private , но и для того, чтобы сделать sharedMyModel final ! Ради будущих читателей, в Swift 3, мы могли бы быть склонны переименовать sharedMyModel быть просто shared .
  • 0
    Это единственный правильный ответ, за исключением того, что переопределение и вызов super.init ошибочны и даже не компилируются.
3

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

class Shared: NSObject {

    static let sharedInstance = Shared()

    private override init() { }
}

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

  • 0
    Почему подкласс NSObject ? Кроме того, это похоже на то же самое, что и stackoverflow.com/a/28436202/1187415 .
3

Просто для справки, вот пример реализации 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()
    }
}
3

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

enum SharedTPScopeManager: TPScopeManager {
  case Singleton
}
  • 0
    ИМО, это единственный правильный способ Swift для реализации Singleton. другие ответы - ObjC / C / C ++
  • 0
    Не могли бы вы уточнить этот ответ? Мне не ясно, где создается экземпляр Singleton из этого фрагмента
Показать ещё 2 комментария
2

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

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

class Singleton {
    static let sharedInstance = Singleton()
}

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

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

Мой способ реализации в 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
2

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

class APIClient {

}

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

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

После просмотра реализации 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. 
}
  • 2
    Как я уже сказал в своих комментариях, единственная причина, по которой это делается, заключается в том, что в какой-то момент в будущем вы можете перемещать / скрывать глобальную переменную и получать более синглтоноподобное поведение. В этот момент, если все использует согласованный шаблон, вы можете просто изменить сами классы-одиночки, не изменяя их использование.
1

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

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
  • 0
    Почему вы использовали инициализацию замыкания, в то время как вы можете напрямую использовать static let sharedInstance = Singleton()
  • 1
    если вы не хотите делать какие-либо дополнительные настройки, то то, что вы говорите, правильно.
1
   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
    Как подробно обсуждалось здесь, в swift оборачивать инициализацию в dispatch_once поскольку инициализация статической переменной является ленивой и автоматически защищается через dispatch_once Apple фактически рекомендует использовать статику вместо dispatch_once по этой причине.
0

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

0

Быстро реализовать синглтон в прошлом - это не что иное, как три способа: глобальные переменные, внутренние переменные и пути 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

Это самый простой вариант с поддержкой потоков. Ни один другой поток не может получить доступ к одному и тому же объекту 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!
        }
    }
}
  • 2
    В чем преимущество статического свойства типа (которое гарантированно будет инициализироваться лениво только один раз, даже при одновременном доступе к нескольким потокам)?
0
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
}}
0

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

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

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

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

0

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

let TEST = Test()

class Test {

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

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

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 и предлагает несколько достоинств:

  • Напоминает, что пользователь не выполняет подклассы
  • Предотвращает создание дополнительных экземпляров
  • Обеспечивает ленивое создание и уникальную инстанцировку
  • Сокращает синтаксис (avoids()), позволяя обращаться к экземпляру как Singleton.instance
-2

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

class MySingletonClass {

    static let sharedSingleton = MySingletonClass()

    private init() {}
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню