Синглтон в 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