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

575

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

class var sharedInstance: TPScopeManager {
    get {
        struct Static {
            static var instance: TPScopeManager? = nil
        }

        if !Static.instance {
            Static.instance = TPScopeManager()
        }

        return Static.instance!
    }
}

Обтекание одноэлементного экземпляра в структуре Static должно позволять одному экземпляру, который не сталкивается с одноэлементными экземплярами, без сложных схем именования, и это должно делать вещи довольно частными. Очевидно, что эта модель не является поточно-ориентированной. Поэтому я попытался добавить 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_onceSwift? Сначала я думал, что проблема с блоком из- ()за сообщения об ошибке, но чем больше я на него смотрю, тем больше думаю, что это может быть вопросом dispatch_once_tправильного определения.

Дэвид Берри
источник
3
Я бы удалил весь этот статический код и использовал бы свойство readonly с инициализатором @lazy.
Sulthan
1
Это то, что я имел в виду. К сожалению, нам все еще не хватает информации о внутренних органах. Однако ИМХО любая реализация @lazyдолжна быть поточно-ориентированной.
Sulthan
1
И этот способ также имеет то преимущество, что не подвергает реализацию хищничеству вызывающих абонентов.
Дэвид Берри
1
Также не похоже, что у вас могут быть переменные класса @lazy.
Дэвид Берри
Быть осторожен! Две вещи, чтобы отметить с этим подходом. Во-первых, любые классы, которые наследуются от этого, должны будут переопределить свойство sharedInstance. Static.instance = TPScopeManager()заставляет тип экземпляра. Если вы используете что-то вроде Static.instance = self()обязательного инициализатора, будет создан соответствующий класс типов. Тем не менее, и это важно отметить, только один раз для всех случаев в иерархии! Первый тип для инициализации - это тип, установленный для всех экземпляров. Я не думаю, что цель-c вела себя так же.
Шон Вудворд

Ответы:

713

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

hpique
источник
13
«Поток безопасен благодаря let» - это было сказано где-нибудь? Я не могу найти упоминание об этом в документации.
Jtbandes
4
@jtbandes Константы являются потокобезопасными на всех языках, которые я знаю.
hpique
2
@DaveWood Я полагаю, вы говорите о последнем подходе. Я процитирую себя: «Я бы сказал, что больше нет необходимости использовать этот подход, но я все равно выложу его здесь, так как я нахожу различия в синтаксисе интересными».
hpique
5
Должны initли быть объявлены privateтакже гарантии того, что один и только один экземпляр объекта будет существовать в течение всего времени жизни приложения?
Андрей
5
В подходе «константа класса» я бы предложил (а) объявить класс таким, finalчтобы вы не делили его на подклассы; и (б) объявление initметода privateтак, что вы не можете случайно создать экземпляр другого экземпляра где-либо.
Роб
175

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

От сюда

David Berry
источник
1
Для подтверждения: глобальные переменные имеют ленивую, поточно-ориентированную инициализацию, а переменные класса - нет. Правильно?
Билл
14
Я бы добавил, что хорошей практикой было бы объявить инициализатор как частный: private init() {}для дальнейшего обеспечения того факта, что этот класс не предназначен для внешнего применения.
Паскаль Бурк
1
Итак, статическая инициализация struct var является ленивой и поточно-ориентированной, что если эта статическая struct var является словарем для мультитонов, то мы должны вручную синхронизировать / ставить в очередь вызовы для каждого доступа, верно?
Если я правильно понимаю ваш вопрос, доступ к словарю и массиву не является поточно-ориентированным, поэтому вам нужно будет использовать некоторую форму синхронизации потоков.
Дэвид Берри
@DavidBerry Как мне вызвать функцию внутри этого синглтон-класса? Мне нужно, чтобы функция вызывалась при первом вызове myClass.sharedInstance.
Амит Дхас,
163

Для Swift 1.2 и выше:

class Singleton  {
   static let sharedInstance = Singleton()
}

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

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

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

Для Swift 1.0 и 1.1:

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

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

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

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

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

Джек
источник
2
Любой, кто читает только этот ответ: не забудьте сделать токен статичным, иначе поведение не определено. Смотрите отредактированный вопрос Дэвида для полного кода.
nschum
@nschum в противном случае, поведение не является неопределенным, оно просто четко определено: блок всегда будет выполняться.
Майкл
@ Майкл: Документация утверждает, что она не определена. Таким образом, текущее поведение является случайным.
nschum
1
Это странная вещь, чтобы сказать. Если документация называет его «неопределенным», это просто означает, что тот, кто написал код, не дает никаких обещаний относительно того, что он делает. Это не имеет ничего общего с кодом, который знает, является ли переменная статической. Это просто означает, что на текущее (или кажущееся) поведение нельзя полагаться.
nschum
6
Вы можете добавить в private init() {}качестве инициализатора SingletonClass. чтобы предотвратить создание экземпляра снаружи.
Ринтаро
46

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

class MySingleton {

    static let sharedMySingleton = MySingleton()

    private init() {
        // ...
    }
}
Florian
источник
35

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

var tpScopeManagerSharedInstance = TPScopeManager()

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

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

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

Крис Геллчи
источник
3
Почему «вар» и много «пусть»?
Стефан
1
Может быть, это может быть, я проверил это только с помощью var.
Крис Геллчи
Мне нравится этот ответ, однако мне нужно получить доступ к нему (Singleton) из Interface Builder. Любая идея о том, как я могу получить доступ к этому tpScopeManagerSharedInstance из IB? Спасибо.
Луис Паласиос
Это мой любимый способ иметь синглтон. Он имеет все обычные функции (потокобезопасность и ленивый экземпляр) и поддерживает очень легкий синтаксис: не нужно писать TPScopeManager.sharedInstance.doIt()все время, просто назовите свой класс TPScopeManagerClass, поместите это объявление рядом с классом public let TPScopeManager = TPScopeManagerClass(), а при использовании - просто пишите TPScopeManager.doIt(). Очень чистый!
Алекс
Здесь нет ничего, что могло бы помешать созданию дополнительных экземпляров TPScopeManager, и поэтому оно не является единичным по определению.
Калеб
28

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

class MyClass {

    private static let _sharedInstance = MyClass()

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

Получить синглтон через MyClass.sharedInstance().

Райан
источник
1
проголосовал за комментарий LearnCocos2D :), а также за стиль.
x4h1d
2
глобальная переменная должна быть изменена на переменную класса через статическое внутри класса.
Малхал
2
@malhal, когда переменная помечена как приватная, но вне класса, она не глобальна - но имеет область видимости только для файла, в котором она находится. Статика внутри класса будет работать примерно так же, но я обновил ответ, чтобы использовать статический как вы предложили, лучше сгруппировать переменную в класс, если вы используете несколько классов в файле.
Райан
1
«Синглтоны Swift представлены в структурах какао как функции класса» ... Не в Swift 3. Теперь они обычно являются staticсвойствами.
Роб
17

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

class Singleton {
    static let sharedInstance = Singleton()
}

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

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

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

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

Свифт 4+

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

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

Глядя на пример кода 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
}
user2485100
источник
Я уверен, что использование статического синтаксиса по умолчанию сделает все надоедливые задания.
Эонил
к сожалению, статика работает только внутри структур, поэтому этот шаблон.
user2485100
Я хотел, чтобы мы не использовали dispatch_onceматериалы. Я делаю ставку на ваш стиль. :)
Эонил
Разве classв объявлении класса нет эквивалента staticв объявлении структуры?
Рассел Борогове
@ Сэм Да, это так. См. Запись в блоге Apple « Файлы и инициализация», в которой разъясняется, что эта dispatch_onceвозможность полезна как глобальным, так и статическим членам структур и перечислений .
Роб
5

Вкратце,

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

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

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

onmyway133
источник
4

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

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

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

[ImageStore sharedStore];

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

Майкл
источник
Это на самом деле более сжато и правильно, чем в другом примере, потому что оно реализовано так же, как и другие синглтоны Swift. т.е. , как функции класса нравятся NSFileManager.defaultManager(), но до сих пор использует ленивые механизмы статического члена поточно-Свифт.
Лесли Годвин
Какао обычно реализует их как статические свойства, а не как функции класса.
Роб
Я знаю об этом, мой комментарий старше 2 лет. Спасибо за упоминание.
Майкл
4

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

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
final class MySingleton {
     private init() {}
     static let shared = MySingleton()
}

Тогда назови это;

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

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

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)")
Kingiol
источник
Это точно так же, как один из ответов, которые я прошел на пути к текущему ответу. Поскольку глобальные переменные инициализируются как ленивыми, так и поточно-ориентированными, нет причин для дополнительной сложности.
Дэвид Берри
@David Кроме глобальной переменной. :)
hpique
@ Нет, точно так же, как одна из моих предыдущих попыток. Посмотрите историю изменений.
Дэвид Берри
4

Лучший подход в Swift выше 1.2 - это однострочный синглтон, так как -

class Shared: NSObject {

    static let sharedInstance = Shared()

    private override init() { }
}

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

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

Из 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
    }()
}
sleepwalkerfx
источник
3

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

enum SharedTPScopeManager: TPScopeManager {
    case Singleton
}
Говард Ловатт
источник
ИМО, это единственный правильный способ Swift для реализации Singleton. другие ответы - путь ObjC / C / C ++
Брайан Чен
Не могли бы вы уточнить этот ответ? Мне непонятно, где
создается
@KennyWinker У меня нет логина для разработчиков Apple, поэтому нет быстрого, и поэтому я не могу ответить, когда происходит инициализация. В Java это первое использование. Возможно, вы могли бы попробовать это с печатью при инициализации и посмотреть, происходит ли печать при запуске или после доступа. Это будет зависеть от того, как enum реализован компилятором.
Говард Ловатт
@KennyWinkler: Apple только уточнить , как это работает, см developer.apple.com/swift/blog/?id=7 . В нем говорится: «запускайте инициализатор для глобала при первом обращении к нему, аналогично Java» и, в частности. Они также говорят, что под прикрытием они используют «dispatch_once, чтобы убедиться, что инициализация является атомарной». Поэтому enum - это почти наверняка путь, если у вас нет какой-то необычной инициализации, тогда решение - статический let.
Говард Ловатт
2

Просто для справки, вот пример реализации Singleton реализации Nested Struct Джека Ву / 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()
    }
}
SchoonSauce
источник
2

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

class AppSingleton: NSObject {

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

    override init() {
        super.init()
    }
}
Вики Праджапати
источник
1

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

class APIClient {

}

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

extension APIClient {
    class func sharedClient() -> APIClient {
        return sharedAPIClient
    }
}
Виктор Радченко
источник
1

Мой способ реализации в 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
user2737730
источник
1

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

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

    private init() {}
}

Доступ

let signleton = Singleton.sharedInstance

Причины:

  • static Свойство type гарантированно будет инициализироваться лениво только один раз, даже при одновременном доступе к нескольким потокам, поэтому нет необходимости использовать dispatch_once
  • Приватизация initметода, поэтому экземпляр не может быть создан другими классами.
  • final класс, поскольку вы не хотите, чтобы другие классы наследовали класс Singleton.
applefreak
источник
Почему вы использовали инициализацию замыкания, в то время как вы можете использовать напрямуюstatic let sharedInstance = Singleton()
abhimuralidharan
1
если вы не хотите делать какие-либо дополнительные настройки, то то, что вы говорите, правильно.
applefreak
1

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

let gScopeManagerSharedInstance = ScopeManager()

class ScopeManager {
   // No need for a class method to return the shared instance. Use the gScopeManagerSharedInstance directly. 
}
Эсса А. Хаддад
источник
2
Как я уже сказал в своих комментариях, единственная причина сделать это состоит в том, что в какой-то момент в будущем вы можете перемещать / скрывать глобальную переменную и получать более синглтоноподобное поведение. В этот момент, если все использует согласованный шаблон, вы можете просто изменить сами классы-одиночки, не изменяя их использование.
Дэвид Берри
0
   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!
}
DD.amor
источник
Как подробно обсуждалось здесь, нет необходимости быстро оборачивать инициализацию, dispatch_onceтак как инициализация статических переменных ленива и автоматически защищена через dispatch_once Apple, фактически рекомендует использовать статику вместо dispatch_once по этой причине.
Дэвид Берри
0

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

Вот два хороших синглтона. (Примечание: независимо от того, какой вид написания должен обращать внимание на метод приватизации 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
Тим
источник
-1

Это самый простой с поточно-ориентированными возможностями. Никакой другой поток не может получить доступ к одному и тому же объекту-одиночке, даже если он хочет. Свифт 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
В чем преимущество статического свойства типа (которое гарантированно будет инициализироваться лениво только один раз, даже при одновременном доступе к нескольким потокам)?
Мартин Р
-1

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

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

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

RicardoDuarte
источник
-2

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

let TEST = Test()

class Test {

    private init() {
        // This is a private (!) constructor
    }
}
Едидия Рейсс
источник
private initбыло уже предложено здесь: stackoverflow.com/a/28436202/1187415 .
Мартин Р
-2

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

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. Сокращает синтаксис (avoids ()), позволяя получить доступ к экземпляру как Singleton.instance
Гонсало Дуранона
источник