swift - Swift 中的单例

标签 swift singleton

我一直在尝试实现一个单例,用作我从网络上传到我的 iOS 应用程序的照片的缓存。我在下面的代码中附加了三个变体。我试图让变体 2 工作,但它导致了我不理解的编译器错误,并希望获得有关我做错了什么的帮助。变体 1 进行缓存,但我不喜欢使用全局变量。变体 3 不执行实际缓存,我相信这是因为我在分配给 var ic = .... 时得到了一份副本,对吗?

我们将不胜感激任何反馈和见解。

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

最佳答案

标准的单例模式是:

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 示例,请参阅 previous revision这个答案。

关于swift - Swift 中的单例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26742138/

相关文章:

c++ - 双重检查线程安全单例和无锁的创建

node.js - 如果导出的模块函数被调用两次,则会抛出错误

ios - 应用程序委托(delegate)事件(例如: applicationWillTerminate) to my custom class

swift - 如何找到 NSTimer 剩余的时间间隔

json - 快速登录到 json API

c++ - 使用单例类从所有类到达

java - 具有多个不同类加载器的单例类

iphone - 在单例中设置字典导致 EXC_BAD_ACCESS

swift - 无缝连接 AVAssets

objective-c - 强制 Product-Swift.h 更新