Синглтон в Swift

Я пытался реализовать синглтон, который будет использоваться в качестве кэша для фотографий, которые я загрузил в свое приложение iOS из интернета. Я прикрепил три варианта кода ниже. Я попытался заставить вариант 2 работать, но он вызывает ошибку компилятора, которую я не понимаю и хотел бы получить помощь в том, что я делаю неправильно. Вариант 1 делает кэширование, но мне не нравится использование глобальной переменной. Вариант 3 не делает фактического кэширования, и я считаю, что это потому, что я получаю копию присвоение var ic = .... это правда?

любые отзывы будет принята с благодарностью.

спасибо, Цви

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

3 ответов


стандартный одноэлементный шаблон:

final class Manager {
    static let shared = Manager()

    private init() { ... }

    func foo() { ... }
}

и вы бы использовали его так:

Manager.shared.foo()

кредит appzYourLife за указание на то, что его следует объявить final чтобы убедиться, что это не случайно подклассы, а также использование private модификатор доступа для инициализатора, чтобы убедиться, что вы случайно не создаете экземпляр другого экземпляра. См.https://stackoverflow.com/a/38793747/1271826.

поэтому, возвращаясь к вашему вопрос кэша изображений, вы бы использовали этот одноэлементный шаблон:

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 subclassed.

    private init() { }

    /// Subscript operator to retrieve and update cache

    subscript(key: String) -> UIImage? {
        get {
            return cache[key]
        }

        set (newValue) {
            cache[key] = newValue
        }
    }
}

затем вы можете:

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

или

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

показав упрощенную реализацию одноэлементного кэша выше, мы должны отметить, что вы, вероятно, хотите (a) сделать его потокобезопасным с помощью NSCache; и (b) реагировать на давление памяти. Таким образом, фактическая реализация примерно следующая в Swift 3:

final class ImageCache: NSCache<AnyObject, UIImage> {

    static let shared = ImageCache()

    /// Observer for `UIApplicationDidReceiveMemoryWarningNotification`.

    private var memoryWarningObserver: NSObjectProtocol!

    /// Note, this is `private` to avoid subclassing this; singletons shouldn't be subclassed.
    ///
    /// 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)
            }
        }
    }

}

и вы бы использовали его как следует:

ImageCache.shared["foo"] = image

и

let image = ImageCache.shared["foo"]

пример Swift 2.3 см. В разделе предыдущей редакции этого ответа.


Ниже приведены два разных подхода для создания вашего одноэлементного класса в swift 2.0

подход 1) Этот подход является объективной реализацией C над 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")
       }
 }

подход 2) одна строка Singleton, не забудьте реализовать частный init (ограничить использование только singleton)

import UIKit

class SomeManager: NSObject {

       static let sharedInstance = SomeManager()

       private override init() {

       }

       func someMethod(){
            print("Some method call")
       }
  }

вызовите метод Singleton, например:

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

вызвать какой-то метод:

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

вызовите некоторый метод, создав новый экземпляр класса (сбой):

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