variable - swift static shared




Utilisation d'un modèle de dispatch_once singleton dans Swift (19)

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 depuis MethodMethod puisque let fait à peu près la même chose qu'une méthode de classe sharedInstance. Tout ce que vous devez faire est de le déclarer comme une constante globale et ce serait le cas.

let gScopeManagerSharedInstance = ScopeManager()

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

J'essaie d'élaborer un modèle singleton approprié pour l'utilisation 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!
    }
}

Envelopper l'instance singleton dans la structure statique devrait permettre à une seule instance de ne pas entrer en collision avec des instances singleton sans schémas de nommage complexes, et cela devrait rendre les choses assez privées. Évidemment cependant, ce modèle n'est pas sûr pour les threads, j'ai donc essayé d'ajouter dispatch_once à l'ensemble:

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

Impossible de convertir le type de l'expression 'Void' en type '()'

J'ai essayé plusieurs variantes de la syntaxe, mais elles semblent toutes avoir le même résultat:

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

Quelle est la bonne utilisation de dispatch_once utilisant Swift? J'ai d'abord pensé que le problème était dû au bloc dû au () dans le message d'erreur, mais plus je le regardais, plus je pense qu'il pourrait être question de définir correctement le dispatch_once_t .


C'est le plus simple avec des capacités de thread sécurisé. Aucun autre thread ne peut accéder au même objet singleton même s'il le souhaite. 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!
        }
    }
}

En bref,

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

Vous pouvez lire les here

L'initialisateur paresseux d'une variable globale (également pour les membres statiques de structures et d'enums) est lancé la première fois que global est accédé, et est lancé en tant que dispatch_once pour s'assurer que l'initialisation est atomique.


En regardant l'exemple de code d'Apple, je suis tombé sur ce modèle. Je ne suis pas sûr de savoir comment Swift traite les statistiques, mais ce serait thread safe en C #. J'inclus à la fois la propriété et la méthode d'interopérabilité Objective-C.

struct StaticRank {
    static let shared = RankMapping()
}

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

class var shared:RankMapping {
    return StaticRank.shared
}

Je préfère cette implémentation:

class APIClient {

}

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

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

Je suggérerais un Enum, comme vous utiliseriez en Java, par exemple:

enum SharedTPScopeManager: TPScopeManager {
  case Singleton
}

Juste pour référence, voici un exemple d'implémentation Singleton de l'implémentation de Nested Struct de Jack Wu / hpique. La mise en œuvre montre également comment l'archivage pourrait fonctionner, ainsi que certaines fonctions d'accompagnement. Je ne pouvais pas trouver cela complet d'un exemple, donc j'espère que cela aide quelqu'un!

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

Et si vous n'avez pas reconnu certaines de ces fonctions, voici un petit fichier utilitaire Swift que j'ai utilisé:

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

La meilleure approche dans Swift au-dessus de 1.2 est un singleton d'une ligne, comme -

class Shared: NSObject {

    static let sharedInstance = Shared()

    private override init() { }
}

Pour plus de détails sur cette approche, vous pouvez visiter ce link .


Mon mode de mise en œuvre dans 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")   

}

Accédez à globalDic à partir de n'importe quel écran de l'application par le ci-dessous.

Lis:

 println(ConfigurationManager.sharedInstance.globalDic)  

Écrire:

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

Puisque Apple a maintenant clarifié que les variables de structure statiques sont initialisées à la fois paresseuses et enveloppées dans dispatch_once (voir la note à la fin de la publication), je pense que ma solution finale sera:

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

        return Singleton.instance
    }
}

Cela profite de l'initialisation automatique paresseuse et sans danger pour les threads des éléments structurels statiques, cache en toute sécurité l'implémentation réelle du consommateur, maintient tout compactement compartimenté pour la lisibilité et élimine une variable globale visible.

Apple a clarifié que l'initialisateur paresseux est sûr pour les threads, il n'y a donc pas besoin de dispatch_once ou de protections similaires

L'initialisateur paresseux d'une variable globale (également pour les membres statiques de structures et d'enums) est lancé la première fois que global est accédé, et est lancé en tant que dispatch_once pour s'assurer que l'initialisation est atomique. Cela permet une manière cool d'utiliser dispatch_once dans votre code: déclarez simplement une variable globale avec un initialiseur et marquez-la private.

D' here


Selon la documentation Apple , il a été répété plusieurs fois que la façon la plus simple de le faire dans Swift est avec une propriété de type statique:

class Singleton {
    static let sharedInstance = Singleton()
}

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 invoquée immédiatement:

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

Ceci est garanti pour être thread-safe et paresseusement initialisé qu'une seule fois.


Si vous envisagez d'utiliser votre classe singleton Swift en Objective-C, cette configuration demandera au compilateur de générer les en-têtes Objective-C appropriés:

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

Ensuite, dans la classe Objective-C, vous pouvez appeler votre singleton comme vous l'avez fait dans les jours précédant Swift:

[ImageStore sharedStore];

Ceci est juste ma mise en œuvre simple.


Utilisation:

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

Comment utiliser:

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

tl; dr: Utilisez l'approche de la constante de classe si vous utilisez Swift 1.2 ou supérieur et l'approche de structure imbriquée si vous avez besoin de supporter des versions antérieures.

D'après mon expérience avec Swift, il existe trois approches pour implémenter le modèle Singleton qui supporte l'initialisation paresseuse et la sécurité des threads.

Constante de classe

class Singleton  {
   static let sharedInstance = Singleton()
}

Cette approche supporte l'initialisation paresseuse parce que Swift paresseux initialise les constantes de classe (et les variables), et est thread safe par la définition de let . Ceci est maintenant officiellement recommandé 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 de structure imbriquée ci-dessous ou une constante globale.

Structure imbriquée

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

Ici, nous utilisons la constante statique d'une structure imbriquée en tant que constante de classe. C'est une solution de contournement pour l'absence de constantes de classe statiques dans Swift 1.1 et versions antérieures, et fonctionne toujours comme une solution de contournement pour l'absence de constantes statiques et de variables dans les fonctions.

dispatch_once

L'approche traditionnelle de l'Objective-C a été transférée à Swift. Je suis à peu près certain qu'il n'y a pas d'avantage sur l'approche de la structure imbriquée, mais je la mets ici de toute façon, car je trouve les différences de syntaxe intéressantes.

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 ce projet GitHub pour les tests unitaires.


Pour Swift 1.2 et au-delà:

class Singleton  {
   static let sharedInstance = Singleton()
}

Avec une preuve d'exactitude (tout le mérite va here ), il n'y a pas ou très peu de raisons d'utiliser l'une des méthodes précédentes pour les singletons.

Mise à jour : Ceci 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 vs class . static devrait être celui à utiliser même lorsque class variables de class deviennent disponibles. Les singletons ne sont pas destinés à être sous-classés, car cela aboutirait à plusieurs instances du singleton de base. L'utilisation de la static fait d'une belle manière Swifty.

Pour Swift 1.0 et 1.1:

Avec les changements récents dans Swift, principalement de nouvelles méthodes de contrôle d'accès, je suis maintenant penché vers la façon 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 l'article du blog Swift here :

L'initialisateur paresseux d'une variable globale (également pour les membres statiques de structures et d'enums) est lancé la première fois que global est accédé, et est lancé en tant que dispatch_once pour s'assurer que l'initialisation est atomique. Cela permet une manière cool d'utiliser dispatch_once dans votre code: déclarez simplement une variable globale avec un initialiseur et marquez-la private.

Cette façon de créer un singleton est sûre, rapide, paresseuse, et également connectée à ObjC gratuitement.


Première solution

let SocketManager = SocketManagerSingleton();

class SocketManagerSingleton {

}

Plus tard dans votre code:

func someFunction() {        
    var socketManager = SocketManager        
}

Deuxième solution

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

class SocketManagerSingleton {

}

Et plus tard dans votre code, vous serez en mesure de garder les accolades pour moins de confusion:

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

J'ai tendance à utiliser la syntaxe suivante comme la plus complète:

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

Cela fonctionne de Swift 1.2 jusqu'à 4, et offre plusieurs vertus:

  1. Rappelle à l'utilisateur de ne pas implémenter la sous-classe
  2. Empêche la création d'instances supplémentaires
  3. Assure une création paresseuse et une instanciation unique
  4. Raccourcit la syntaxe (avoidids ()) en permettant d'accéder à l'instance Singleton.instance

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

private var sharedURLCacheForRequestsKey:Void?
extension URLCache{
public static func sharedURLCacheForRequests()->URLCache{
    var cache = objc_getAssociatedObject(OperationQueue.main, &sharedURLCacheForRequestsKey)
    if cache is URLCache {

    }else{
        cache = URLCache(memoryCapacity: 0, diskCapacity: 1*1024*1024*1024, diskPath: "sharedURLCacheForRequestsKey")
        objc_setAssociatedObject(OperationQueue.main, &sharedURLCacheForRequestsKey, cache, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)

    }
    return cache as! URLCache
}}




dispatch