Solteirão em Swift

Eu tenho tentado implementar um singleton para ser usado como um cache para fotos que eu carreguei para o meu aplicativo iOS da web. Eu anexei três variantes no código abaixo. Eu tentei fazer a variante 2 funcionar, mas ela está causando um erro no compilador que eu não entendo e gostaria de obter ajuda sobre o que estou fazendo errado. Variante 1 faz o cache, mas eu não gosto do uso de uma variável global. A variante 3 não faz o cache real e acredito que seja porque estou obtendo uma cópia na atribuição para var ic = …., está correto?

Qualquer feedback e insight serão muito apreciados.

Obrigado, 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") } } 

O padrão singleton padrão é:

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

E você usaria assim:

 Manager.shared.foo() 

Agradecemos a appzYourLife por apontar que deve ser declarado final para garantir que ele não seja subclassificado acidentalmente, bem como o uso do modificador de access private para o inicializador, para garantir que você não instancie acidentalmente outra instância. Veja https://stackoverflow.com/a/38793747/1271826 .

Então, voltando à sua pergunta do cache de imagens, você usaria esse padrão 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 } } } 

Então você pode:

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

Ou

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

Tendo mostrado uma implementação de cache singleton simplista acima, devemos observar que você provavelmente deseja (a) tornar o thread seguro usando o NSCache ; e (b) responder à pressão da memory. Então, a implementação real é algo como o seguinte no 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 você o usaria da seguinte maneira:

 ImageCache.shared["foo"] = image 

E

 let image = ImageCache.shared["foo"] 

Para o exemplo do Swift 2.3, veja a revisão anterior desta resposta.

A seguir estão as duas abordagens diferentes para criar sua class singleton em swift 2.0

Abordagem 1) Essa abordagem é a implementação do Objetivo C mais rápida.

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

Abordagem 2) Uma linha Singleton, não se esqueça de implementar o init privado (restringir o uso de apenas singleton)

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

Chame o método Singleton como:

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

Invoque algum método:

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

Invoque algum método criando uma nova instância de class (falha):

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