589 votes

dispatch_once modèle de singleton dans swift

Je suis en train de mettre au point un singleton modèle pour l'utilisation de Swift. Jusqu'à présent, j'ai été en mesure d'obtenir un non-thread-safe modèle de travail en tant que:

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

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

        return Static.instance!
    }
}

Habillage de l'instance du singleton dans la Statique de la structure doit permettre à une instance unique qui n'entrent pas en collision avec singleton instances sans complexe de nommage schemings, et devrait rendre les choses assez privé. De toute évidence cependant, ce modèle n'est pas thread-safe, donc j'ai essayé d'ajouter dispatch_once à l'ensemble de la chose:

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 du compilateur sur la dispatch_once ligne:

Cannot convert the expression's type 'Void' to type '()'

J'ai essayé plusieurs différentes variantes de la syntaxe, mais ils semblent tous avoir les mêmes résultats:

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

Quelqu'un sait ce que le bon usage de l' dispatch_once est l'utilisation de swift? J'ai d'abord pensé que le problème était avec le bloc en raison de l' () dans le message d'erreur, mais plus je la regarde et plus je pense que c'est peut être une question de l' dispatch_once_t correctement défini.

726voto

hpique Points 23090

tl;dr: Utiliser le imbriquée struct approche décrite ci-dessous.

De ma courte expérience avec Swift il y a trois méthodes pour implémenter le pattern Singleton qui prennent en charge l'initialisation tardive et du fil de sécurité.

Ces approches pourrait changer ou devenir redondant que la langue évolue.

Constante globale

private let _SingletonSharedInstance = Singleton()

class Singleton  {
    class var sharedInstance : Singleton {
        return _SingletonSharedInstance
    }
}

Nous utilisons une constante globale parce que les constantes de classe ne sont pas encore pris en charge.

Cette approche prend en charge l'initialisation tardive parce que Swift paresseusement initialise les constantes globales (et variables), et est thread-safe, en vertu de l' let.

Imbriquée struct

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

Contrairement aux classes, les structures en charge d'constantes statiques. À l'aide d'un imbriquée struct nous pouvons tirer parti de sa constante statique comme une constante de classe.

Le imbriquée struct est l'approche que je recommande jusqu'à ce que les constantes de classe sont pris en charge.

dispatch_once

Le traditionnel Objective-C approche porté à Swift. Je suis assez certain que il n'y a aucun avantage sur le imbriquée struct approche, mais je suis en train de mettre ici de toute façon comme je l'ai trouver les différences dans la syntaxe intéressant.

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

Voir cette GitHub du projet pour les tests unitaires.

175voto

David Points 8433

Mis à jour avec les éclaircissements de la part de Apple

Depuis Apple a désormais clair que static struct variables sont initialisées à la fois paresseux et enveloppé dans dispatch_once (voir la note à la fin du post), je pense que ma solution finale va être:

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

        return Singleton.instance
    }
}

Cette méthode tire parti de l'automatique paresseux, thread-safe initialisation static struct éléments en toute sécurité, se cache la mise en œuvre effective de la consommation, permet de tout de façon compacte compartimentée pour la lisibilité, et élimine visible variable globale.

Plus Tôt Solutions

En prenant @Krisgellci la réponse de compte, ce que je vais enfin le vent, c'est ce qui est ci-dessous. (En ignorant le code de test que j'ai utilisé pour établir qu'il semble être paresseux chargé et thread-safe) la Plupart de la différence est à l'aide d'un mécanisme qui est plus conforme et à un certain point, espérons-le, permettrait de me cacher le magasin de sauvegarde de l'global lors de l' @privé est pris en charge.

class GlobalVariable {
    class var sharedInstance:GlobalVariable {
        return GlobalVariableSharedInstance
    }

    // The rest of this class is merely for testing purposes
    func ping(message:String) {
        NSLog("ping:%@", message)
    }

    init() {
        NSLog("init");
        sleep(5)
    }
}

let GlobalVariableSharedInstance = GlobalVariable()

Et le code que j'ai utilisé pour vérifier le filetage de la sécurité et de l'initialisation tardive:

class ViewController: UIViewController {

    override func viewDidLoad() {

        NSLog("viewDidLoad")

        super.viewDidLoad()

        for i in 0..5 {
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)) {
                NSLog("thread")
                GlobalVariable.sharedInstance.ping("thread")
            }
        }

        GlobalVariable.sharedInstance.ping("viewDidLoad")
    }
}

BTW, à l'aide de NSLog ici au lieu de l'println parce que println n'est pas thread-safe :)

Apple a précisé que paresseux initialiseur sont thread-safe:

Le paresseux initialiseur pour une variable globale (également pour les membres statiques de structures et les énumérations) est exécutée la première fois que le mondial est accessible, et il est lancé en tant que dispatch_once pour s'assurer que l'initialisation est atomique. Cette mesure est une façon cool pour utiliser dispatch_once dans votre code: il suffit de déclarer une variable globale avec un initialiseur et marquer privé.

À partir d' ici

163voto

Jack Wu Points 5016

Edit pour Xcode Beta 5

Avec les récents changements de Swift, la plupart des nouvelles méthodes de contrôle d'accès. Je suis maintenant penché vers le moyen le plus propre d'utiliser une variable globale pour les singletons.

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

Comme mentionné dans la Swift article du blog ici:

Le paresseux initialiseur pour une variable globale (également pour les membres statiques de les structures et les énumérations) est exécutée la première fois que le mondial est accessible, et est lancé comme dispatch_once pour s'assurer que l'initialisation est atomique. Cette mesure est une façon cool pour utiliser dispatch_once dans votre code: il suffit de déclarer une variable globale avec un initialiseur et marquer privé.

Cette façon de créer un singleton est thread-safe, rapide, paresseux, et également relié à ObjC gratuitement.

35voto

Kris Gellci Points 2463

Il y a une meilleure façon de le faire. Vous pouvez déclarer une variable globale dans votre classe au-dessus de la déclaration de classe comme suit

Cela demande juste votre défaut init ou n’importe quel init et les variables globales sont dispatch_once par défaut dans Swift. Alors dans quelle que soit la classe que vous voulez obtenir une référence, vous juste procédez comme suit :

Donc en gros, vous pouvez vous débarrasser de l’ensemble du bloc de code instance partagée.

8voto

user2485100 Points 31

Regarder les exemples de code Apple que je suis tombé sur ce modèle. Je ne sais pas comment swift traite de statique, mais c’est thread-safe en c#. J’inclus aussi bien les propriétés et les méthodes pour ObjC interop.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X