J'essaie de trouver un modèle singleton approprié à utiliser dans Swift. Jusqu'à présent, j'ai pu obtenir un modèle non thread-safe fonctionnant comme:
class var sharedInstance: TPScopeManager {
get {
struct Static {
static var instance: TPScopeManager? = nil
}
if !Static.instance {
Static.instance = TPScopeManager()
}
return Static.instance!
}
}
L'encapsulation de l'instance singleton dans la structure statique devrait permettre une seule instance qui n'entre pas en collision avec des instances singleton sans schémas de nommage complexes, et devrait rendre les choses assez privées. Évidemment, ce modèle n'est pas thread-safe. J'ai donc essayé d'ajouter dispatch_once
à tout cela:
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!
}
}
Mais j'obtiens une erreur de compilation sur la dispatch_once
ligne:
Impossible de convertir le type de l'expression 'Void' en type '()'
J'ai essayé plusieurs variantes différentes de la syntaxe, mais elles semblent toutes avoir les mêmes résultats:
dispatch_once(Static.token, { Static.instance = TPScopeManager() })
Quelle est la bonne utilisation de dispatch_once
Swift? Au début, je pensais que le problème était avec le bloc en raison du ()
message d'erreur, mais plus je le regarde, plus je pense qu'il peut être question d'obtenir le dispatch_once_t
correctement défini.
@lazy
devrait être thread-safe.Static.instance = TPScopeManager()
force le type d'instance. Si vous utilisez quelque chose commeStatic.instance = self()
avec un initialiseur requis, la classe de type appropriée sera générée. Même ainsi, et c'est la chose importante à noter, une seule fois pour toutes les instances de la hiérarchie! Le premier type à initialiser est le type défini pour toutes les instances. Je ne pense pas que l'objectif-c se soit comporté de la même façon.Réponses:
tl; dr: utilisez l' approche constante de classe si vous utilisez Swift 1.2 ou supérieur et l' approche structurée imbriquée si vous devez prendre en charge les versions antérieures.
D'après mon expérience avec Swift, il existe trois approches pour implémenter le modèle Singleton qui prennent en charge l'initialisation paresseuse et la sécurité des threads.
Constante de classe
Cette approche prend en charge l'initialisation paresseuse car Swift initialise paresseusement les constantes de classe (et les variables) et est thread-safe par la définition de
let
. Il s'agit désormais de la méthode officiellement recommandée pour instancier un singleton.Les constantes de classe ont été introduites dans Swift 1.2. Si vous devez prendre en charge une version antérieure de Swift, utilisez l'approche structurée imbriquée ci-dessous ou une constante globale.
Structure imbriquée
Ici, nous utilisons la constante statique d'une structure imbriquée comme constante de classe. Il s'agit d'une solution de contournement pour le manque de constantes de classe statiques dans Swift 1.1 et versions antérieures, et fonctionne toujours comme solution de contournement pour le manque de constantes statiques et de variables dans les fonctions.
dispatch_once
L'approche Objective-C traditionnelle portait sur Swift. Je suis assez certain qu'il n'y a aucun avantage sur l'approche de structure imbriquée mais je le mets quand même ici car je trouve les différences de syntaxe intéressantes.
Voir ce projet GitHub pour les tests unitaires.
la source
init
il également être déclaréprivate
pour garantir qu'une et une seule instance de l'objet existera pendant la durée de vie de l'application?final
ne pas la sous- classer ; et (b) déclarer lainit
méthode pourprivate
que vous ne puissiez pas instancier accidentellement une autre instance quelque part.Depuis qu'Apple a maintenant clarifié que les variables structurées statiques sont initialisées à la fois paresseuses et enveloppées
dispatch_once
(voir la note à la fin de l'article), je pense que ma solution finale sera:Cela profite de l'initialisation automatique paresseuse et thread-safe des éléments de structure statiques, cache en toute sécurité l'implémentation réelle du consommateur, conserve tout compactement compartimenté pour la lisibilité et élimine une variable globale visible.
Apple a précisé que les initialiseurs paresseux sont thread-safe, il n'y a donc pas besoin de
dispatch_once
protections similairesD' ici
la source
private init() {}
pour renforcer davantage le fait que cette classe n'est pas destinée à être instanciée extérieurement.Pour Swift 1.2 et au-delà:
Avec une preuve d'exactitude (tout le mérite revient ici ), il n'y a guère de raison maintenant d'utiliser l'une des méthodes précédentes pour les singletons.
Mise à jour : c'est maintenant la façon officielle de définir les singletons comme décrit dans les documents officiels !
En ce qui concerne les préoccupations sur l' utilisation
static
vsclass
.static
devrait être celui à utiliser même lorsque lesclass
variables deviennent disponibles. Les singletons ne sont pas censés être sous-classés car cela entraînerait plusieurs instances du singleton de base. L'utilisationstatic
applique cela d'une manière magnifique et Swifty.Pour Swift 1.0 et 1.1:
Avec les récents changements dans Swift, principalement de nouvelles méthodes de contrôle d'accès, je penche maintenant vers une manière plus propre d'utiliser une variable globale pour les singletons.
Comme mentionné dans l'article du blog Swift ici :
Cette façon de créer un singleton est sûre pour les threads, rapide, paresseuse et également reliée à ObjC gratuitement.
la source
private init() {}
comme initialiseur deSingletonClass
. pour éviter d'instancier de l'extérieur.Swift 1.2 ou version ultérieure prend désormais en charge les variables / constantes statiques dans les classes. Vous pouvez donc simplement utiliser une constante statique:
la source
Il existe une meilleure façon de procéder. Vous pouvez déclarer une variable globale dans votre classe au-dessus de la déclaration de classe comme ceci:
Cela appelle simplement votre init par défaut ou les variables init et globales
dispatch_once
par défaut dans Swift. Ensuite, dans la classe pour laquelle vous souhaitez obtenir une référence, il vous suffit de faire ceci:Donc, fondamentalement, vous pouvez vous débarrasser de tout le bloc de code d'instance partagé.
la source
TPScopeManager.sharedInstance.doIt()
tout le temps, il suffit de nommer votre classeTPScopeManagerClass
, d'avoir cette déclaration à côté de la classepublic let TPScopeManager = TPScopeManagerClass()
, et lorsque vous utilisez simplement writeTPScopeManager.doIt()
. Très propre!TPScopeManager
, et ce n'est donc pas un singleton par définition.Singletons Swift sont exposés dans les cadres de cacao comme des fonctions de classe, par exemple
NSFileManager.defaultManager()
,NSNotificationCenter.defaultCenter()
. Il est donc plus logique en tant que fonction de classe de refléter ce comportement, plutôt qu'en tant que variable de classe comme d'autres solutions. par exemple:Récupérez le singleton via
MyClass.sharedInstance()
.la source
static
propriétés.Selon la documentation Apple , il a été répété à plusieurs reprises que la façon la plus simple de le faire dans Swift est avec une propriété de type statique:
Cependant, si vous cherchez un moyen d'effectuer une configuration supplémentaire au-delà d'un simple appel de constructeur, le secret est d'utiliser une fermeture immédiatement invoquée:
Ceci est garanti pour être thread-safe et initialisé paresseusement une seule fois.
la source
Swift 4+
la source
En regardant l'exemple de code d'Apple, je suis tombé sur ce modèle. Je ne sais pas comment Swift gère la statique, mais ce serait sûr pour les threads en C #. J'inclus à la fois la propriété et la méthode d'interopérabilité Objective-C.
la source
dispatch_once
étoffes. Je parie sur ton style. :)class
dans une déclaration de classe l'équivalent d'static
une déclaration struct?dispatch_once
capacité.En bref,
Vous voudrez peut-être lire les fichiers et l'initialisation
la source
Si vous prévoyez d'utiliser votre classe Swift singleton dans Objective-C, cette configuration fera en sorte que le compilateur génère des en-têtes appropriés de type Objective-C:
Ensuite, dans la classe Objective-C, vous pouvez appeler votre singleton comme vous l'avez fait dans les jours pré-Swift:
Ceci est juste ma mise en œuvre simple.
la source
NSFileManager.defaultManager()
, mais utilisent toujours les mécanismes de membre statique paresseux thread-safe de Swift.Première solution
Plus loin dans votre code:
Deuxième solution
Et plus tard dans votre code, vous pourrez garder les accolades pour moins de confusion:
la source
Appelez-le ensuite;
la source
init
commeprivate
, mais aussi pour faire lesharedMyModel
asfinal
! Pour le plaisir des futurs lecteurs, dans Swift 3, nous pourrions être enclins à renommersharedMyModel
simplementshared
.Utilisation:
Comment utiliser:
la source
La meilleure approche dans Swift au-dessus de 1.2 est un singleton d'une ligne, comme -
Pour en savoir plus sur cette approche, vous pouvez visiter ce lien .
la source
NSObject
sous - classe?. En dehors de cela, cela semble être essentiellement le même que stackoverflow.com/a/28436202/1187415 .Depuis Apple Docs (Swift 3.0.1),
la source
Je suggérerais un
enum
, comme vous utiliseriez en Java, par exemplela source
Juste pour référence, voici un exemple d'implémentation Singleton de l'implémentation Nested Struct de Jack Wu / hpique. L'implémentation montre également comment l'archivage pourrait fonctionner, ainsi que certaines fonctions associées. Je n'ai pas pu trouver cet exemple complet, alors j'espère que cela aide quelqu'un!
Et si vous n'avez pas reconnu certaines de ces fonctions, voici un petit fichier utilitaire Swift vivant que j'utilise:
la source
Dans swift, vous pouvez créer une classe singleton de la manière suivante:
la source
Je préfère cette implémentation:
la source
Ma façon d'implémenter dans Swift ...
ConfigurationManager.swift
Accédez au globalDic à partir de n'importe quel écran de l'application par ce qui suit.
Lis:
Écrire:
la source
La seule bonne approche est ci-dessous.
Accéder
Les raisons:
static
La propriété de type est garantie d'être initialisée paresseusement une seule fois, même lorsqu'elle est accessible sur plusieurs threads simultanément, donc pas besoin d'utiliserdispatch_once
init
méthode afin que l'instance ne puisse pas être créée par d'autres classes.final
car vous ne voulez pas que d'autres classes héritent de la classe Singleton.la source
static let sharedInstance = Singleton()
Après avoir vu l'implémentation de David, il semble qu'il n'y ait pas besoin d'avoir une fonction de classe singleton
instanceMethod
carlet
cela fait à peu près la même chose qu'unesharedInstance
méthode de classe. Tout ce que vous avez à faire est de le déclarer comme une constante globale et ce serait tout.la source
la source
dispatch_once
car l'initialisation des variables statiques est paresseuse et automatiquement protégée viadispatch_once
Apple recommande en fait d'utiliser la statique au lieu de dispatch_once pour cette raison.Swift pour réaliser singleton dans le passé, n'est rien de plus que les trois façons: les variables globales, les variables internes et les méthodes dispatch_once.
Voici deux bons singleton. (Remarque: quel que soit le type d'écriture, il faudra prêter attention à la méthode de privatisation init () .Parce que dans Swift, tout le constructeur par défaut de l'objet est public, doit être réécrit init peut être transformé en privé , empêche les autres objets de cette classe '()' par méthode d'initialisation par défaut de créer l'objet.)
Méthode 1:
Méthode 2:
la source
C'est le plus simple avec des capacités thread-safe. Aucun autre thread ne peut accéder au même objet singleton même s'il le souhaite. Swift 3/4
la source
J'ai exigé que mon singleton autorise l'héritage, et aucune de ces solutions ne le permettait réellement. J'ai donc trouvé ceci:
Singleton.sharedInstance()
première exécution, il retournera l'instance deSingleton
SubSingleton.sharedInstance()
première, il retournera l'instance deSubSingleton
created.SubSingleton.sharedInstance()
estSingleton
est vrai et la même instance est utilisé.Le problème avec cette première approche sale est que je ne peux pas garantir que les sous-classes implémenteront le
dispatch_once_t
et s'assurer qu'ilsharedInstanceVar
n'est modifié qu'une fois par classe.J'essaierai de l'affiner davantage, mais il serait intéressant de voir si quelqu'un a de forts sentiments contre cela (outre le fait qu'il est verbeux et nécessite de le mettre à jour manuellement).
la source
Ceci est ma mise en œuvre. Cela empêche également le programmeur de créer une nouvelle instance:
la source
private init
a déjà été suggéré ici: stackoverflow.com/a/28436202/1187415 .J'utilise la syntaxe suivante:
Cela fonctionne de Swift 1.2 à 4, et présente plusieurs avantages:
Singleton.instance
la source