7
\$\begingroup\$

I am caching an image locally..That same image will be used in few screens. Steps for doing this is as follows:

  1. Get image from cache

  2. If not present in NSCache, get image from Cache directory

  3. If not present in Cache Directory, download from Internet

  4. Save downloaded image to dir

  5. Insert the image in cache

//Helper class is as

     class ImageCacheHelper:NSObject{

        static var cache = NSCache()
        static var isNotRunningDispatch:Bool = true

      class func setObjectForKey(imageData:NSData,imageKey:String){

            ImageCacheHelper.cache.setObject(imageData, forKey: imageKey)

        }

      class func getObjectForKey(imageKey:String)->NSData?{

            return ImageCacheHelper.cache.objectForKey(imageKey) as? NSData

        }

      class func getImage(imageUrl:String,completionHandler:(NSData)->()){        
            if ImageCacheHelper.isNotRunningDispatch{

                ImageCacheHelper.isNotRunningDispatch = false
                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), {
                    let imgUrl = NSURL(string:imageUrl)
                    let imageData = NSData(contentsOfURL: imgUrl!)!
                    ImageCacheHelper.setObjectForKey(imageData, imageKey: "\(imageUrl.hashValue)")
                    ImageCacheHelper.isNotRunningDispatch = true
                    completionHandler(imageData)

                })
            }else{
               print("alerady started loading image")
           }
        }
      }

//How i call this from ViewController class is as

let userImageUrl = "\(appBaseUrl)\(imageUrlString)"
let fileManager = NSFileManager.defaultManager()
let diskPaths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomainMask.UserDomainMask, true)
let cacheDirectory = diskPaths[0] as NSString
let diskPath = cacheDirectory.stringByAppendingPathComponent("\(userImageUrl.hashValue)")
if let data = ImageCacheHelper.getObjectForKey("\(userImageUrl.hashValue)"){
          let userImage =  UIImage(data: data)
          self.userImgView.image = userImage

}else if fileManager.fileExistsAtPath(diskPath){

          let image =  UIImage(contentsOfFile: diskPath)
          self.userImgView.image = image
          ImageCacheHelper.setObjectForKey(UIImageJPEGRepresentation(image, 1.0)!, imageKey: "\(userImageUrl.hashValue)")

}else   {
          ImageCacheHelper.getImage(userImageUrl){ imageData in

            imageData.writeToFile(diskPath, atomically: true)
                    dispatch_async(dispatch_get_main_queue()){

                        let userImage =  UIImage(data: imageData)
                        self.userImgView.image = userImage

                    }
        }
}

Flaws That I see in my code are as follows:

  1. Creating a static variable for NSCache.

  2. Would be great if settingObject in NSCache, getting would be as of NSUserDefaults.

How to optimize this?

Here is a snippet of project that i have tried to do https://drive.google.com/file/d/0B6dTvD1JbkgBS1k3Ry1xNmZ1VHc/view?usp=sharing

\$\endgroup\$
  • \$\begingroup\$ Where in your code is an image downloaded from the Internet? \$\endgroup\$ – Martin R Jan 5 '16 at 18:21
  • \$\begingroup\$ @MartinR sorry that was just anothe snippet of code..please see the edited question \$\endgroup\$ – LC 웃 Jan 5 '16 at 18:31
2
\$\begingroup\$

Modify Caching Strategy

It seems every image that is subject to caching is ending up both in memory and on disk. Depending on the number of images and caching aggressiveness, this could be more expensive (in terms of resource usage) than the network request used to retrieve the image.

From my perspective, using both NSCache and the Cache directory together doesn’t add any value. They are both volatile and unpredictable (to different extents). The system could reclaim space from either when it deems it necessary.

From Apples File System Programming Guide:

"Note that the system may delete the Caches/ directory to free up disk space, so your app must be able to re-create or download these files as needed.”

Therefore its either a choice between caching in memory and caching on disk or an emphasis of one over the other. This inevitably leads to additional questions about how your app will be used and the what kind of images you are working with.

Some basic guidelines:

  • You don’t want to store a large number of full resolution images in memory
  • You don’t want to have too many file system I/O operations (spread over time)
  • You don’t want to have images in memory that are not going to be seen by the user
  • You don’t want to go to disk every time for frequently seen images

What is a large number? How much is too many? You will have to test and measure.

Test & Measure (and then measure again)

I would definitely recommend testing and measuring your caching strategy to see how much of an impact it has. Xcode instruments provides tools to accomplish this.

One way to go about this is:

  1. Test with no cache (this will be your baseline)
  2. Test with a simple caching strategy (maybe just using NSCache or the Cache directory)
  3. Compare the results of step 2 to your baseline obtained from step 1
  4. Not satisfied, then apply a different caching strategy. Repeat step 3. Otherwise, if you are satisfied with the resource usage and responsiveness of the app then you can stop here until there are significant changes to either your codebase, data or the iOS frameworks.
\$\endgroup\$
  • \$\begingroup\$ Thanks..long time since i posted a question..But nice guidelines \$\endgroup\$ – LC 웃 Nov 22 '16 at 7:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.