2014-11-04 7 views
8

मैं वेब से अपने आईओएस ऐप पर अपलोड की गई तस्वीरों के लिए कैश के रूप में उपयोग करने के लिए एक सिंगलटन को कार्यान्वित करने का प्रयास कर रहा हूं। मैंने नीचे दिए गए कोड में तीन प्रकार संलग्न किए हैं। मैंने संस्करण 2 काम करने की कोशिश की लेकिन यह एक कंपाइलर त्रुटि उत्पन्न कर रहा है जिसे मैं समझ नहीं पा रहा हूं और मैं क्या गलत कर रहा हूं इस पर सहायता प्राप्त करना चाहता हूं। संस्करण 1 कैशिंग करता है लेकिन मुझे वैश्विक चर के उपयोग को पसंद नहीं है। संस्करण 3 वास्तविक कैशिंग नहीं करता है और मेरा मानना ​​है कि ऐसा इसलिए है क्योंकि मुझे var ic = .... के असाइनमेंट में प्रतिलिपि मिल रही है, क्या यह सही है?स्विफ्ट में सिंगलटन

कोई भी प्रतिक्रिया और अंतर्दृष्टि की सराहना की जाएगी।

धन्यवाद, जेड vi

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

तुम क्यों प्रयोग कर रहे हैं '[स्ट्रिंग:? UIImage]' बजाय 'की [स्ट्रिंग : UIImage] '? मुझे लगता है कि शायद आपके पास होने वाली परेशानी का स्रोत है। –

+0

ध्यान दें कि आप 'dispatch_once' उपयोग करने की आवश्यकता नहीं है - पढ़ [यहां] (http://stackoverflow.com/a/26376288/148357) – Antonio

+0

सिर्फ FTR, DLImageLoader एक अविश्वसनीय कैशिंग पुस्तकालय है, और स्विफ्ट में है। यह अमूल्य ... – Fattie

उत्तर

20

मानक सिंगलटन पैटर्न है:

final class Manager { 
    static let shared = Manager() 

    private init() { ... } 

    func foo() { ... } 
} 

और तुम इतना है कि यह प्रयोग करना होगा: उनका कहना है के लिए appzYourLife को

Manager.shared.foo() 

क्रेडिट यह सुनिश्चित करने के लिए कि इसे गलती से एससीसी नहीं है, उसे 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"] 

एक साधारण से पता चला के बाद ऊपर सिंगलटन कैश कार्यान्वयन, हमें ध्यान रखना चाहिए कि आप शायद (ए) इसे थ्रेड से सुरक्षित बनाना चाहते हैं NSCache गाओ; और (बी) स्मृति दबाव का जवाब। तो, वास्तविक क्रियान्वयन स्विफ्ट 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"] 

स्विफ्ट 2.3 उदाहरण के लिए, इस उत्तर के previous revision देखें।

+1

रोब है, तो आप बहुत बहुत धन्यवाद! – zvweiss

+0

धन्यवाद रोब, आपका समाधान बहुत अच्छा है। क्या आपको पता है कि उन्हें लॉन्च के बीच रखना संभव है? उदाहरण के लिए NSUserDefaults के साथ? मुझे यकीन नहीं है कि यह सबसे अच्छा समाधान है। –

+1

@MarieDm - 'NSUserDefaults' शायद नहीं सामान की इस तरह के लिए सही जगह है। मैं मैन्युअल रूप से '.CachesDirectory' लिए सामग्री लिखते हैं (या तो' NSFileManager' विधि 'URLForDirectory' या भीतर' NSSearchPathForDirectoriesInDomains' से)। BTW, यह जवाब स्विफ्ट 1.2 predated, लेकिन मैं स्विफ्ट के वर्तमान संस्करण में संभव सरल सिंगलटन पैटर्न के साथ यह संशोधित किया है। – Rob

1

के बाद दो अलग दृष्टिकोण तेजी से 2.0

दृष्टिकोण 1 में अपने सिंगलटन वर्ग बनाने के लिए) यह दृष्टिकोण तेजी से अधिक उद्देश्य सी कार्यान्वयन है कर रहे हैं।

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) एक पंक्ति सिंगलटन, निजी init (केवल सिंगलटन का उपयोग प्रतिबंधित)

import UIKit 

class SomeManager: NSObject { 

     static let sharedInstance = SomeManager() 

     private override init() { 

     } 

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

कॉल को लागू करने के लिए मत भूलना सिंगलटन विधि की तरह:

SomeManager.sharedInstance.someMethod() 
0

स्विफ्ट 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 
संबंधित मुद्दे