Usando um modelo singleton dispatch_once no Swift


575

Estou tentando elaborar um modelo singleton apropriado para uso no Swift. Até agora, consegui obter um modelo seguro sem thread trabalhando como:

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

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

        return Static.instance!
    }
}

O agrupamento da instância singleton na estrutura estática deve permitir uma única instância que não colide com instâncias singleton sem esquemas de nomenclatura complexos e deve tornar as coisas bastante privadas. Obviamente, porém, este modelo não é seguro para threads. Então, eu tentei adicionar dispatch_onceà coisa toda:

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!
    }
}

Mas eu recebo um erro de compilador na dispatch_oncelinha:

Não é possível converter o tipo da expressão 'Void' em type '()'

Eu tentei várias variantes diferentes da sintaxe, mas todas parecem ter os mesmos resultados:

dispatch_once(Static.token, { Static.instance = TPScopeManager() })

Qual é o uso adequado do dispatch_onceSwift? Inicialmente, pensei que o problema estava no bloco devido à ()mensagem de erro, mas quanto mais olho para ele, mais acho que pode ser uma questão de dispatch_once_tdefinir corretamente.


3
Eu removeria todo esse código estático e usaria uma propriedade somente leitura com um inicializador @lazy.
Sulthan

1
Foi isso que eu quis dizer. Infelizmente ainda não temos informações suficientes sobre os internos. No entanto, IMHO qualquer implementação de @lazydeve ser thread-safe.
Sulthan

1
E dessa maneira também tem a vantagem de não expor a implementação às predações dos chamadores.
David Berry

1
Também não parece que você possa ter variáveis ​​de classe @lazy.
David Berry

Seja cuidadoso! Duas coisas a serem observadas com essa abordagem. Primeiro, qualquer classe que herda disso terá que substituir a propriedade sharedInstance. Static.instance = TPScopeManager()força o tipo de instância. Se você usar algo como Static.instance = self()um inicializador necessário, a classe de tipo apropriada será gerada. Mesmo assim, e isso é importante, apenas uma vez para todas as instâncias da hierarquia! O primeiro tipo a inicializar é o tipo definido para todas as instâncias. Eu não acho que o objetivo-c se comportou da mesma maneira.
Sean Woodward

Respostas:


713

tl; dr: Use a abordagem de classe constante se você estiver usando o Swift 1.2 ou superior e a abordagem de estrutura aninhada se precisar oferecer suporte a versões anteriores.

Da minha experiência com Swift, existem três abordagens para implementar o padrão Singleton que oferecem suporte à inicialização lenta e à segurança de threads.

Constante de classe

class Singleton  {
   static let sharedInstance = Singleton()
}

Essa abordagem suporta a inicialização lenta porque o Swift inicializa preguiçosamente constantes de classe (e variáveis) e é seguro para threads pela definição de let. Agora é agora oficialmente recomendada a maneira de instanciar um singleton.

As constantes de classe foram introduzidas no Swift 1.2. Se você precisar oferecer suporte a uma versão anterior do Swift, use a abordagem de estrutura aninhada abaixo ou uma constante global.

Estrutura aninhada

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

Aqui estamos usando a constante estática de uma estrutura aninhada como uma constante de classe. Esta é uma solução alternativa para a falta de constantes de classe estática no Swift 1.1 e versões anteriores e ainda funciona como uma solução alternativa para a falta de constantes e variáveis ​​estáticas nas funções.

dispatch_once

A abordagem tradicional de Objective-C foi portada para Swift. Estou bastante certo de que não há vantagem sobre a abordagem de estrutura aninhada, mas estou colocando aqui de qualquer maneira, pois acho as diferenças de sintaxe interessantes.

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!
    }
}

Veja este projeto do GitHub para testes de unidade.


13
"thread safe em virtude de let" - isso já foi afirmado em algum lugar? Não consigo encontrar menção a isso na documentação.
Jtbandes

4
As constantes @jtbandes são seguras para threads em todos os idiomas que conheço.
Hpique

2
@DaveWood Suponho que você esteja falando sobre a última abordagem. Vou me citar: "Diria que não é mais necessário usar essa abordagem, mas estou colocando aqui de qualquer maneira, pois acho as diferenças de sintaxe interessantes".
Hpique

5
initTambém deve ser declarado privatepara garantir que uma e apenas uma instância do objeto existirá ao longo da vida útil do aplicativo?
26515 Andrew

5
Na abordagem "Classe constante", sugiro (a) declarar a classe para finalque você não a subclasse; e (b) declarar o initmétodo para privateque você não possa instanciar acidentalmente outra instância em algum lugar.
Rob

175

Como a Apple agora esclareceu que as variáveis ​​de estrutura estática são inicializadas preguiçosamente e agrupadas dispatch_once(veja a nota no final do post), acho que minha solução final será:

class WithSingleton {
    class var sharedInstance: WithSingleton {
        struct Singleton {
            static let instance = WithSingleton()
        }

        return Singleton.instance
    }
}

Isso tira proveito da inicialização automática preguiçosa e segura de thread de elementos estáticos, oculta com segurança a implementação real do consumidor, mantém tudo compactamente compartimentado para maior legibilidade e elimina uma variável global visível.

A Apple esclareceu que o inicializador lento é seguro para threads, portanto não há necessidade de dispatch_onceproteções semelhantes

O inicializador lento para uma variável global (também para membros estáticos de estruturas e enumerações) é executado na primeira vez que global é acessado e é iniciado como dispatch_once para garantir que a inicialização seja atômica. Isso permite uma maneira legal de usar dispatch_once no seu código: apenas declare uma variável global com um inicializador e marque-a como privada.

A partir daqui


1
Para confirmar: variáveis ​​globais têm inicialização lenta e segura para threads, mas variáveis ​​de classe não. Direita?
Bill

14
Eu acrescentaria que uma boa prática seria declarar o inicializador como privado private init() {}:, para reforçar ainda mais o fato de que essa classe não deve ser instanciada externamente.
Pascal Bourque 26/09

1
para que a inicialização estática do struct var seja preguiçosa e segura para threads, e se esse static static var for um dicionário para várias toneladas, teremos que sincronizar / enfileirar manualmente as chamadas para cada acesso, certo?

Se entendi sua pergunta corretamente, os acessos a dicionários e matrizes não são inerentemente seguros para threads, portanto, você precisará usar alguma forma de sincronização de threads.
David Berry

@DavidBerry Como devo chamar uma função dentro dessa classe singleton? Preciso que uma função seja chamada na primeira chamada de myClass.sharedInstance.
Ameet Dhas

163

Para o Swift 1.2 e além:

class Singleton  {
   static let sharedInstance = Singleton()
}

Com uma prova de correção (todo o crédito é válido aqui ), agora há pouco ou nenhum motivo para usar qualquer um dos métodos anteriores para singletons.

Atualização : agora é a maneira oficial de definir singletons, conforme descrito nos documentos oficiais !

Quanto às preocupações sobre o uso staticvs class. staticdeve ser o único a usar, mesmo quando as classvariáveis ​​se tornam disponíveis. Singletons não devem ser subclassificados, pois isso resultaria em várias instâncias do singleton base. O uso staticreforça isso de uma maneira bonita e rápida.

Para Swift 1.0 e 1.1:

Com as recentes mudanças no Swift, principalmente os novos métodos de controle de acesso, agora estou me inclinando para uma maneira mais limpa de usar uma variável global para singletons.

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

Como mencionado no artigo do blog Swift aqui :

O inicializador lento para uma variável global (também para membros estáticos de estruturas e enumerações) é executado na primeira vez que global é acessado e é iniciado como dispatch_once para garantir que a inicialização seja atômica. Isso permite uma maneira legal de usar dispatch_once no seu código: apenas declare uma variável global com um inicializador e marque-a como privada.

Essa maneira de criar um singleton é segura, rápida, preguiçosa e também é conectada ao ObjC gratuitamente.


2
Qualquer pessoa que esteja lendo apenas esta resposta: lembre-se de tornar o token estático, caso contrário, o comportamento será indefinido. Veja a pergunta editada de David para o código completo.
Nschum

@nschum caso contrário, o comportamento não é indefinido, é apenas quebrado de uma maneira bem definida: o bloco sempre será executado.
Michael Michael

@ Michael: A documentação afirma que é indefinido. O comportamento atual é, portanto, coincidente.
Nschum

1
É uma coisa estranha de se dizer. Se a documentação o chama de "indefinido", isso significa que quem escreveu o código não faz promessas para o que faz. Não tem nada a ver com o código, sabendo se a variável é estática. Significa apenas que o comportamento atual (ou aparente) não pode ser invocado.
Nschum

6
Você pode querer adicionar private init() {}como inicializador de SingletonClass. para impedir instanciar de fora.
rintaro 24/09

46

O Swift 1.2 ou posterior agora suporta variáveis ​​estáticas / constantes nas classes. Então você pode simplesmente usar uma constante estática:

class MySingleton {

    static let sharedMySingleton = MySingleton()

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

35

Existe uma maneira melhor de fazer isso. Você pode declarar uma variável global em sua classe acima da declaração de classe desta forma:

var tpScopeManagerSharedInstance = TPScopeManager()

Isso apenas chama seu init padrão ou quaisquer variáveis ​​globais e init dispatch_oncepor padrão no Swift. Em qualquer classe que você deseja obter uma referência, basta fazer o seguinte:

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

Então, basicamente, você pode se livrar de todo o bloco de código de instância compartilhada.


3
Por que um "var" e muito um "let"?
Stephan

1
talvez possa ser um let, eu só testei com uma var.
Kris Gellci

Eu gosto desta resposta, no entanto, preciso acessar isso (Singleton) no Interface Builder. Alguma idéia de como eu poderia acessar esse tpScopeManagerSharedInstance no IB ?. Thanks.-
Luis Palacios

Esta é a minha maneira preferida de ter um singleton. Possui todos os recursos usuais (segurança de thread e instanciação lenta) e suporta uma sintaxe muito leve: não há necessidade de escrever TPScopeManager.sharedInstance.doIt()o tempo todo, basta nomear sua classe TPScopeManagerClass, ter esta declaração ao lado da classe public let TPScopeManager = TPScopeManagerClass()e, ao usar apenas escrever TPScopeManager.doIt(). Muito limpo!
Alex

Não há nada aqui para impedir a criação de instâncias adicionais de TPScopeManager, e, portanto, não é um singleton por definição.
Caleb

28

Swift únicos estão expostos nos quadros de cacau como funções de classe, por exemplo NSFileManager.defaultManager(), NSNotificationCenter.defaultCenter(). Portanto, faz mais sentido, como uma função de classe, espelhar esse comportamento, em vez de uma variável de classe, como algumas outras soluções. por exemplo:

class MyClass {

    private static let _sharedInstance = MyClass()

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

Recupere o singleton via MyClass.sharedInstance().


1
votado pelo comentário de LearnCocos2D :), também pelo estilo.
X4h1d

2
a variável global deve ser alterada para uma variável de classe através de uma estática dentro da classe.
malhal

2
@malhal quando uma variável é marcada como privada, mas fora de uma classe, ela não é global - mas com escopo apenas para o arquivo em que está. Uma estática dentro da classe funcionaria da mesma forma, mas atualizei a resposta para usar a estática como você sugeriu, pois agrupa melhor a variável na classe se você usar várias classes dentro do arquivo.
Ryan

1
"Os Singletons Swift são expostos nas estruturas de cacau como funções de classe" ... Não no Swift 3. Agora eles são geralmente staticpropriedades.
Rob

17

De acordo com a documentação da Apple , foi repetido várias vezes que a maneira mais fácil de fazer isso no Swift é com uma propriedade de tipo estático:

class Singleton {
    static let sharedInstance = Singleton()
}

No entanto, se você estiver procurando uma maneira de executar configurações adicionais além de uma simples chamada de construtor, o segredo é usar um fechamento imediatamente invocado:

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

Isso garante a segurança de threads e é inicializado preguiçosamente apenas uma vez.


como você pode definir a instância let estática de volta para zero?
Gpichler #

1
@ user1463853 - Você não pode, e geralmente não deveria.
Rob

16

Swift 4+

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

final class Kraken: Singleton {
    static let sharedInstance = Kraken()
    private init() {}
}

2
isso precisa classe final, você pode explicar mais a diferença, porque eu tenho problema com a outra solução de Singleton com struct
Raheel Sadiq

essa substituição deve ser privada init () {}
NSRover 13/16/16

8

Olhando para o código de exemplo da Apple, me deparei com esse padrão. Não tenho certeza de como o Swift lida com estática, mas isso seria seguro para threads em C #. Incluo a propriedade e o método para interoperabilidade Objective-C.

struct StaticRank {
    static let shared = RankMapping()
}

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

class var shared:RankMapping {
    return StaticRank.shared
}

Tenho certeza de que apenas o uso dessa sintaxe estática padrão fará todos os trabalhos irritantes.
Eonil

infelizmente, as estáticas funcionam apenas dentro de estruturas, e é por isso que esse padrão.
precisa saber é o seguinte

Minha intenção era que não dispatch_onceprecisássemos usar animais. Eu estou apostando no seu estilo. :)
Eonil

classDentro de uma declaração de classe não é equivalente a staticuma declaração struct?
Russell Borogove

@ Sam Sim, é. Veja a entrada do blog da Apple em Arquivos e inicialização, que deixa claro que membros globais e estáticos de estruturas e enumerações se beneficiam desse dispatch_oncerecurso.
Rob

5

Em resumo,

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

Você pode querer ler Arquivos e Inicialização

O inicializador lento para uma variável global (também para membros estáticos de estruturas e enumerações) é executado na primeira vez que o global é acessado e é iniciado dispatch_oncepara garantir que a inicialização seja atômica.


4

Se você planeja usar sua classe de singleton Swift no Objective-C, essa configuração fará com que o compilador gere cabeçalhos apropriados do tipo Objective-C:

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

Em seguida, na classe Objective-C, você pode chamar seu singleton da maneira que fez nos dias pré-Swift:

[ImageStore sharedStore];

Esta é apenas minha implementação simples.


Na verdade, isso é mais conciso e correto que o outro exemplo, porque é implementado da mesma maneira que outros singletons Swift. ou seja: como funções de classe gosta NSFileManager.defaultManager(), mas ainda usa os mecanismos membros estático thread-safe preguiçosos de Swift.
Leslie Godwin #

O cacau geralmente os implementa como propriedades estáticas, atualmente, não como funções de classe.
Rob

Estou ciente disso, meu comentário tem mais de 2 anos. Obrigado por mencionar.
Michael

4

Primeira solução

let SocketManager = SocketManagerSingleton();

class SocketManagerSingleton {

}

Posteriormente no seu código:

func someFunction() {        
    var socketManager = SocketManager        
}

Segunda solução

func SocketManager() -> SocketManagerSingleton {
    return _SocketManager
}
let _SocketManager = SocketManagerSingleton();

class SocketManagerSingleton {

}

E mais tarde no seu código, você será capaz de manter chaves para menos confusão:

func someFunction() {        
    var socketManager = SocketManager()        
}

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

Então chame;

let shared = MySingleton.shared

Bem feito, não apenas para marcar initcomo private, mas também para fazer o sharedMyModelas final! Para o bem dos futuros leitores, em Swift 3, podemos estar inclinados a renomear sharedMyModelcomo simples shared.
Rob

Esta é a única resposta correta, exceto que a substituição e a chamada para super.init são errôneas e nem serão compiladas.
Michael Morris

4

Usar:

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!
        }
    }
}

Como usar:

UtilSingleton.shareInstance.iVal++
println("singleton new iVal = \(UtilSingleton.shareInstance.iVal)")

É exatamente o mesmo que uma das respostas pelas quais passei no caminho para a resposta atual. Como as variáveis ​​globais são inicializadas preguiçosamente e com segurança para threads, não há razão para a complexidade adicional.
David Berry

@ David Diferente de não ter uma variável global. :)
hpique

@ hpique não, exatamente como uma das minhas tentativas anteriores. Veja o histórico de edições.
David Berry

4

A melhor abordagem no Swift acima de 1,2 é um singleton de uma linha, pois -

class Shared: NSObject {

    static let sharedInstance = Shared()

    private override init() { }
}

Para saber mais detalhes sobre essa abordagem, visite este link .


Por que uma NSObjectsubclasse? Além disso, parece ser essencialmente o mesmo que stackoverflow.com/a/28436202/1187415 .
Martin R

3

No Apple Docs (Swift 3.0.1),

Você pode simplesmente usar uma propriedade de tipo estático, que garante a inicialização lenta apenas uma vez, mesmo quando acessado por vários threads simultaneamente:

class Singleton {
    static let sharedInstance = Singleton()
}

Se você precisar executar configurações adicionais além da inicialização, poderá atribuir o resultado da chamada de um fechamento à constante global:

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

3

Eu sugeriria um enum, como você usaria em Java, por exemplo

enum SharedTPScopeManager: TPScopeManager {
    case Singleton
}

Na IMO, essa é a única maneira correta de Swift de implementar o Singleton. outras respostas são ObjC C / C way / ++
Bryan Chen

Você poderia elaborar esta resposta? Não é claro para mim onde Singleton é instanciado a partir deste trecho
Kenny Winker

@KennyWinker Eu não tenho um login de desenvolvedor da Apple, portanto não é rápido e, portanto, não posso responder quando a inicialização ocorre. Em Java, ele é usado pela primeira vez. Talvez você possa tentar com uma impressão na inicialização e ver se a impressão ocorre no lançamento ou após o acesso. Depende de como a enumeração é implementada pelo compilador.
Howard Lovatt

@KennyWinkler: A Apple acabou de esclarecer como isso funciona, consulte developer.apple.com/swift/blog/?id=7 . Nele, eles dizem "execute o inicializador por um global na primeira vez em que for referenciado, semelhante ao Java" e, em particular. Eles também dizem que, sob as capas, estão usando "dispatch_once para garantir que a inicialização seja atômica". Portanto, o enum é quase certamente o caminho a menos, a menos que você tenha algum init sofisticado para fazer, então um let estático privado é a solução.
Howard Lovatt

2

Apenas para referência, aqui está um exemplo de implementação Singleton da implementação Nested Struct de Jack Wu / hpique. A implementação também mostra como o arquivamento poderia funcionar, bem como algumas funções associadas. Não consegui encontrar este exemplo completo, por isso espero que ajude alguém!

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

E se você não reconheceu algumas dessas funções, aqui está um pequeno arquivo utilitário do Swift que estou usando:

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

No swift, você pode criar uma classe singleton da seguinte maneira:

class AppSingleton: NSObject {

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

    override init() {
        super.init()
    }
}

1

Eu prefiro esta implementação:

class APIClient {

}

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

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

1

Minha maneira de implementar no 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")   

}

Acesse o globalDic em qualquer tela do aplicativo abaixo.

Ler:

 println(ConfigurationManager.sharedInstance.globalDic)  

Escreva:

 ConfigurationManager.sharedInstance.globalDic = tmpDic // tmpDict is any value that to be shared among the application

1

A única abordagem correta está abaixo.

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

    private init() {}
}

Acessar

let signleton = Singleton.sharedInstance

Razões:

  • static A propriedade type é garantida para ser inicializada lentamente apenas uma vez, mesmo quando acessada por vários threads simultaneamente, portanto, não é necessário usar dispatch_once
  • Privatizando o initmétodo para que a instância não possa ser criada por outras classes.
  • final classe como você não deseja que outras classes herdem a classe Singleton.

Por que você usou a inicialização fechamento, enquanto você pode usar diretamentestatic let sharedInstance = Singleton()
abhimuralidharan

1
se você não quiser fazer nenhuma configuração adicional, o que você está dizendo está certo.
Applefreak 19/03/19

1

Depois de ver a implementação de David, parece que não há necessidade de ter uma função de classe singleton, instanceMethodpois letestá fazendo praticamente a mesma coisa que um sharedInstancemétodo de classe. Tudo o que você precisa fazer é declará-lo como uma constante global e seria isso.

let gScopeManagerSharedInstance = ScopeManager()

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

2
Como eu disse em meus comentários, a única razão para fazer isso é que, em algum momento no futuro, você poderá mover / ocultar a variável global e obter mais comportamentos semelhantes a singleton. Nesse ponto, se tudo estiver usando um padrão consistente, você poderá alterar as próprias classes singleton sem precisar alterar o uso.
9788 David Berry #

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!
}

Como foi discutido detalhadamente aqui, não é necessário agilizar a inicialização, dispatch_oncepois a inicialização de variável estática é lenta e protegida automaticamente pela dispatch_once Apple, na verdade, recomenda o uso de estática em vez de dispatch_once por esse motivo.
David Berry

0

Swift para perceber singleton no passado, nada mais é do que as três maneiras: variáveis ​​globais, variáveis ​​internas e expedição_ de uma maneira.

Aqui estão dois bons singleton. (Nota: não importa que tipo de escrita deve prestar atenção ao método init () de privatização.Por causa do Swift, todo o padrão de construtor do objeto é público, precisa ser reescrito init pode ser transformado em private , impeça outros objetos dessa classe '()' por método de inicialização padrão para criar o objeto.)

Método 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()

Método 2:

class AppManager {
    static let sharedInstance = AppManager()

    private init() {} // Privatizing the init method
}

// How to use?
AppManager.sharedInstance

-1

Este é o mais simples, com recursos seguros para threads. Nenhum outro encadeamento pode acessar o mesmo objeto singleton, mesmo que deseje. 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
Qual é a vantagem sobre uma propriedade de tipo estático (que garante a inicialização lenta apenas uma vez, mesmo quando acessado por vários threads simultaneamente)?
Martin R

-1

Eu exigi que meu singleton permitisse a herança, e nenhuma dessas soluções permitia. Então, eu vim com isso:

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
    }
}
  • Dessa forma, ao fazer o Singleton.sharedInstance()primeiro, retornará a instância deSingleton
  • Ao fazer o SubSingleton.sharedInstance()primeiro, retornará a instância de SubSingletoncreated.
  • Se o acima for feito, então SubSingleton.sharedInstance()é Singletoné verdadeira e a mesma instância é utilizada.

O problema com essa primeira abordagem suja é que não posso garantir que as subclasses implementem dispatch_once_te assegurem que isso sharedInstanceVarseja modificado apenas uma vez por classe.

Vou tentar refinar isso ainda mais, mas seria interessante ver se alguém tem fortes sentimentos contra isso (além do fato de que é detalhado e requer atualizá-lo manualmente).



-2

Eu uso a seguinte sintaxe:

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

Isso funciona do Swift 1.2 até o 4 e tem várias vantagens:

  1. Lembra o usuário de não subclassificar a implementação
  2. Impede a criação de instâncias adicionais
  3. Garante criação lenta e instanciação única
  4. Reduz a sintaxe (evita ()) permitindo acessar a instância como Singleton.instance
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.