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_once
linha:
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_once
Swift? 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_t
definir corretamente.
@lazy
deve ser thread-safe.Static.instance = TPScopeManager()
força o tipo de instância. Se você usar algo comoStatic.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.Respostas:
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
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
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.
Veja este projeto do GitHub para testes de unidade.
fonte
init
Também deve ser declaradoprivate
para garantir que uma e apenas uma instância do objeto existirá ao longo da vida útil do aplicativo?final
que você não a subclasse; e (b) declarar oinit
método paraprivate
que você não possa instanciar acidentalmente outra instância em algum lugar.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á: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_once
proteções semelhantesA partir daqui
fonte
private init() {}
:, para reforçar ainda mais o fato de que essa classe não deve ser instanciada externamente.Para o Swift 1.2 e além:
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
static
vsclass
.static
deve ser o único a usar, mesmo quando asclass
variáveis se tornam disponíveis. Singletons não devem ser subclassificados, pois isso resultaria em várias instâncias do singleton base. O usostatic
reforç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.
Como mencionado no artigo do blog Swift aqui :
Essa maneira de criar um singleton é segura, rápida, preguiçosa e também é conectada ao ObjC gratuitamente.
fonte
private init() {}
como inicializador deSingletonClass
. para impedir instanciar de fora.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:
fonte
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:
Isso apenas chama seu init padrão ou quaisquer variáveis globais e init
dispatch_once
por padrão no Swift. Em qualquer classe que você deseja obter uma referência, basta fazer o seguinte:Então, basicamente, você pode se livrar de todo o bloco de código de instância compartilhada.
fonte
TPScopeManager.sharedInstance.doIt()
o tempo todo, basta nomear sua classeTPScopeManagerClass
, ter esta declaração ao lado da classepublic let TPScopeManager = TPScopeManagerClass()
e, ao usar apenas escreverTPScopeManager.doIt()
. Muito limpo!TPScopeManager
, e, portanto, não é um singleton por definição.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:Recupere o singleton via
MyClass.sharedInstance()
.fonte
static
propriedades.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:
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:
Isso garante a segurança de threads e é inicializado preguiçosamente apenas uma vez.
fonte
Swift 4+
fonte
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.
fonte
dispatch_once
precisássemos usar animais. Eu estou apostando no seu estilo. :)class
Dentro de uma declaração de classe não é equivalente astatic
uma declaração struct?dispatch_once
recurso.Em resumo,
Você pode querer ler Arquivos e Inicialização
fonte
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:
Em seguida, na classe Objective-C, você pode chamar seu singleton da maneira que fez nos dias pré-Swift:
Esta é apenas minha implementação simples.
fonte
NSFileManager.defaultManager()
, mas ainda usa os mecanismos membros estático thread-safe preguiçosos de Swift.Primeira solução
Posteriormente no seu código:
Segunda solução
E mais tarde no seu código, você será capaz de manter chaves para menos confusão:
fonte
Então chame;
fonte
init
comoprivate
, mas também para fazer osharedMyModel
asfinal
! Para o bem dos futuros leitores, em Swift 3, podemos estar inclinados a renomearsharedMyModel
como simplesshared
.Usar:
Como usar:
fonte
A melhor abordagem no Swift acima de 1,2 é um singleton de uma linha, pois -
Para saber mais detalhes sobre essa abordagem, visite este link .
fonte
NSObject
subclasse? Além disso, parece ser essencialmente o mesmo que stackoverflow.com/a/28436202/1187415 .No Apple Docs (Swift 3.0.1),
fonte
Eu sugeriria um
enum
, como você usaria em Java, por exemplofonte
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!
E se você não reconheceu algumas dessas funções, aqui está um pequeno arquivo utilitário do Swift que estou usando:
fonte
No swift, você pode criar uma classe singleton da seguinte maneira:
fonte
Eu prefiro esta implementação:
fonte
Minha maneira de implementar no Swift ...
ConfigurationManager.swift
Acesse o globalDic em qualquer tela do aplicativo abaixo.
Ler:
Escreva:
fonte
A única abordagem correta está abaixo.
Acessar
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 usardispatch_once
init
mé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.fonte
static let sharedInstance = Singleton()
Depois de ver a implementação de David, parece que não há necessidade de ter uma função de classe singleton,
instanceMethod
poislet
está fazendo praticamente a mesma coisa que umsharedInstance
método de classe. Tudo o que você precisa fazer é declará-lo como uma constante global e seria isso.fonte
fonte
dispatch_once
pois a inicialização de variável estática é lenta e protegida automaticamente peladispatch_once
Apple, na verdade, recomenda o uso de estática em vez de dispatch_once por esse motivo.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:
Método 2:
fonte
Este é o mais simples, com recursos seguros para threads. Nenhum outro encadeamento pode acessar o mesmo objeto singleton, mesmo que deseje. Swift 3/4
fonte
Eu exigi que meu singleton permitisse a herança, e nenhuma dessas soluções permitia. Então, eu vim com isso:
Singleton.sharedInstance()
primeiro, retornará a instância deSingleton
SubSingleton.sharedInstance()
primeiro, retornará a instância deSubSingleton
created.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_t
e assegurem que issosharedInstanceVar
seja 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).
fonte
Esta é a minha implementação. Também impede que o programador crie uma nova instância:
fonte
private init
já foi sugerido aqui: stackoverflow.com/a/28436202/1187415 .Eu uso a seguinte sintaxe:
Isso funciona do Swift 1.2 até o 4 e tem várias vantagens:
Singleton.instance
fonte