Singleton in Swift

Ho cercato di implementare un singleton da utilizzare come cache per le foto che ho caricato sulla mia app iOS dal web. Ho allegato tre varianti nel codice qui sotto. Ho provato a far funzionare la variante 2 ma sta causando un errore del compilatore che non capisco e vorrei ricevere aiuto su cosa sto facendo male. Variante 1 fa il caching ma non mi piace l’uso di una variabile globale. La variante 3 non fa il caching effettivo e credo che sia perché sto ottenendo una copia nel compito di var ic = …., è corretto?

Qualsiasi feedback e intuizione sarà molto apprezzato.

Grazie, Zvi

import UIKit private var imageCache: [String: UIImage?] = [String : UIImage?]() class ImageCache { class var imageCache: [String : UIImage?] { struct Static { static var instance: [String : UIImage?]? static var token: dispatch_once_t = 0 } dispatch_once(&Static.token) { Static.instance = [String : UIImage?]() } return Static.instance! } } class ViewController: UIViewController { @IBOutlet weak var imageView: UIImageView! override func viewDidLoad() { super.viewDidLoad() imageView.image = UIImage(data: NSData(contentsOfURL: NSURL(string: "http://images.apple.com/v/iphone-5s/gallery/a/images/download/photo_1.jpg")!)!) //variant 1 - this code is working imageCache["photo_1"] = imageView.image NSLog(imageCache["photo_1"] == nil ? "no good" : "cached") //variant 2 - causing a compiler error on next line: '@lvalue $T7' is not identical to '(String, UIImage?)' //ImageCache.imageCache["photo_1"] = imageView.image //NSLog(ImageCache.imageCache["photo_1"] == nil ? "no good" : "cached") //variant 3 - not doing the caching //var ic = ImageCache.imageCache //ic["photo_1)"] = imageView.image //NSLog(ImageCache.imageCache["photo_1"] == nil ? "no good" : "cached") } } 

Il modello standard di singleton è:

 final class Manager { static let shared = Manager() private init() { ... } func foo() { ... } } 

E lo useresti in questo modo:

 Manager.shared.foo() 

Ringraziamo appzYourLife per aver sottolineato che si dovrebbe dichiararlo final per assicurarsi che non sia accidentalmente sottoclass e che si utilizzi il modificatore di accesso private per l’inizializzatore, per assicurarsi di non istanziare accidentalmente un’altra istanza. Vedi https://stackoverflow.com/a/38793747/1271826 .

Quindi, tornando alla domanda della cache immagine, useresti questo modello singleton:

 final class ImageCache { static let shared = ImageCache() /// Private image cache. private var cache = [String: UIImage]() // Note, this is `private` to avoid subclassing this; singletons shouldn't be subclassd. private init() { } /// Subscript operator to retrieve and update cache subscript(key: String) -> UIImage? { get { return cache[key] } set (newValue) { cache[key] = newValue } } } 

Allora puoi:

 ImageCache.shared["photo1"] = image let image2 = ImageCache.shared["photo2"]) 

O

 let cache = ImageCache.shared cache["photo1"] = image let image2 = cache["photo2"] 

Avendo mostrato una semplicistica implementazione della cache di singleton, dovremmo notare che probabilmente si desidera (a) renderlo sicuro da thread usando NSCache ; e (b) rispondere alla pressione della memoria. Pertanto, l’implementazione effettiva è simile alla seguente in Swift 3:

 final class ImageCache: NSCache { static let shared = ImageCache() /// Observer for `UIApplicationDidReceiveMemoryWarningNotification`. private var memoryWarningObserver: NSObjectProtocol! /// Note, this is `private` to avoid subclassing this; singletons shouldn't be subclassd. /// /// Add observer to purge cache upon memory pressure. private override init() { super.init() memoryWarningObserver = NotificationCenter.default.addObserver(forName: .UIApplicationDidReceiveMemoryWarning, object: nil, queue: nil) { [weak self] notification in self?.removeAllObjects() } } /// The singleton will never be deallocated, but as a matter of defensive programming (in case this is /// later refactored to not be a singleton), let's remove the observer if deallocated. deinit { NotificationCenter.default.removeObserver(memoryWarningObserver) } /// Subscript operation to retrieve and update subscript(key: String) -> UIImage? { get { return object(forKey: key as AnyObject) } set (newValue) { if let object = newValue { setObject(object, forKey: key as AnyObject) } else { removeObject(forKey: key as AnyObject) } } } } 

E lo useresti come segue:

 ImageCache.shared["foo"] = image 

E

 let image = ImageCache.shared["foo"] 

Per l’esempio di Swift 2.3, vedere la revisione precedente di questa risposta.

Di seguito sono riportati i due diversi approcci per creare la class singleton in swift 2.0

Approccio 1) Questo approccio è l’implementazione di Objective C su swift.

 import UIKit class SomeManager: NSObject { class var sharedInstance : SomeManager { struct managerStruct { static var onceToken : dispatch_once_t = 0 static var sharedObject : SomeManager? = nil } dispatch_once(&managerStruct.onceToken) { () -> Void in managerStruct.sharedObject = SomeManager() } return managerStruct.sharedObject! } func someMethod(){ print("Some method call") } } 

Approccio 2) Una riga Singleton, Non dimenticare di implementare l’init privato (limitare l’utilizzo di solo singleton)

 import UIKit class SomeManager: NSObject { static let sharedInstance = SomeManager() private override init() { } func someMethod(){ print("Some method call") } } 

Chiama il metodo Singleton come:

  SomeManager.sharedInstance.someMethod() 

Swift 3:

 class SomeClass { static let sharedInstance = SomeClass() fileprivate override init() { //This prevents others from using the default '()' initializer super.init() } func sayHello() { print("Hello!") } } 

Invoca alcuni metodi:

 SomeClass.sharedInstance.sayHello() //--> "Hello" 

Invoca alcuni metodi creando una nuova istanza di class (non riuscita):

 SomeClass().sayHello() //--> 'SomeClass' cannot be constructed it has no accessible initailizers