How to Use Nscache

How to use NSCache

You use it the same way you would use NSMutableDictionary. The difference is that when NSCache detects excessive memory pressure (i.e. it's caching too many values) it will release some of those values to make room.

If you can recreate those values at runtime (by downloading from the Internet, by doing calculations, whatever) then NSCache may suit your needs. If the data cannot be recreated (e.g. it's user input, it is time-sensitive, etc.) then you should not store it in an NSCache because it will be destroyed there.

Example, not taking thread safety into account:

// Your cache should have a lifetime beyond the method or handful of methods
// that use it. For example, you could make it a field of your application
// delegate, or of your view controller, or something like that. Up to you.
NSCache *myCache = ...;
NSAssert(myCache != nil, @"cache object is missing");

// Try to get the existing object out of the cache, if it's there.
Widget *myWidget = [myCache objectForKey: @"Important Widget"];
if (!myWidget) {
// It's not in the cache yet, or has been removed. We have to
// create it. Presumably, creation is an expensive operation,
// which is why we cache the results. If creation is cheap, we
// probably don't need to bother caching it. That's a design
// decision you'll have to make yourself.
myWidget = [[[Widget alloc] initExpensively] autorelease];

// Put it in the cache. It will stay there as long as the OS
// has room for it. It may be removed at any time, however,
// at which point we'll have to create it again on next use.
[myCache setObject: myWidget forKey: @"Important Widget"];
}

// myWidget should exist now either way. Use it here.
if (myWidget) {
[myWidget runOrWhatever];
}

How to put image to NSCache in Swift?

You can use something like this:

private let cache = NSCache()
.....
func downloadImage(url: String, handler: @escaping(Data?, Error?) -> Void){
let cacheID = NSString(string: url)

if let cachedData = cache.object(forKey: cacheID) {
handler((cachedData as Data), nil)
}else{
if let url = URL(string: url) {
let session = URLSession(configuration: urlSessionConfig)
var request = URLRequest(url: url)
request.cachePolicy = .returnCacheDataElseLoad
request.httpMethod = "get"
session.dataTask(with: request) { (data, response, error) in
if let _data = data {
self.cache.setObject(_data as NSData, forKey: cacheID)
handler(_data, nil)
}else{
handler(nil, error)
}
}.resume()
} else {
// NetworkError is a custom error
handler(nil, NetworkError.invalidURL)
}
}
}
}

How to cache array of Doubles in Swift

As mentioned by others, NSCache is an objC type and will only work with NSObject subclasses. You can use type bridging into NSArray for this.

let cache = NSCache()
let doubleArray: [Double] = [2.0, 3.0, 4.0]
cache.setObject(doubleArray as NSArray, forKey: "key")

// ...

if let doubleArray = cache.object(forKey: "key") as? [Double] {
// Got my array back
}

NSCache Doesn't work with all images when loading for the first time

Here the images are downloading and stored in cache just fine. The problem lies in the updation of tableview cells.

When the table view is loading the cells on to the table the images are not downloaded yet. But once the image is downloaded we have to selectively update the cell so that the image is displayed instantly.

Since you are scrolling , the tableview calls 'cellForRowatIndexpath' again which updates the cell showing the downloaded images while scrolling.

If you still wish to use the extension , I suggest you add the tableView and indexpath as the parameters so that we can call reload specific row and have the view updated instantly.

I have updated the table reload code and structure of the function defined in extension. Let me know how it goes.

let imageCache = NSCache()
var imageURLString : String?

extension UIImageView {


public func imageFromServerURL(urlString: String, tableView : UITableView, indexpath : IndexPath)) {
imageURLString = urlString

if let url = URL(string: urlString) {

image = nil


if let imageFromCache = imageCache.object(forKey: urlString as AnyObject) as? UIImage {

self.image = imageFromCache

return
}

URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in

if error != nil{
print(error as Any)


return
}

DispatchQueue.main.async(execute: {

if let imgaeToCache = UIImage(data: data!){

if imageURLString == urlString {
self.image = imgaeToCache
}

imageCache.setObject(imgaeToCache, forKey: urlString as AnyObject)// calls when scrolling

tableView.reloadRows(at: [indexpath], with: .automatic)

}
})
}) .resume()
}
}

Does the different way of handling key between NSMutableDictionary and NSCache (copy vs. retain) result in different consequence?

The key here (ha) is in the assignment to dicKey/cacheKey. Specifically, the assignments

dicKey = "changedKey"
cacheKey = "changedKey"

are not changing the value of the original dicKey and cacheKey instances, but creating new string objects and setting the local variables to point to these new objects.


In the dictionary case:

  1. dicKey points to an object K₁ whose value is "key"
  2. mutableDic.setObject("one", forKey: dicKey) copies dicKey into a new key object K₂; K₁ is left alone
  3. dicKey = "changedKey" creates a new object K₃ with the value "changedKey", and assigns dicKey to point to it
    • Since nothing points to K₁ anymore, its reference count goes to 0 and the object is deallocated
  4. mutableDic.setObject("two", forKey: dicKey) copies dicKey into a new key object K₄; K₂ is left alone

The end result is that the dictionary contains K₂ and K₄, while dicKey points to K₃.


In the cache case:

  1. dicKey points to an object K₁ whose value is "key"
  2. cache.setObject("one", forKey: cacheKey) retains K₁ for insertion into the cache
  3. cacheKey = "changedKey" creates a new object K₂ with a value "changedKey" and assigns cacheKey to point to it
    • Since the cache is still retaining K₁, it remains alive and in memory, even if dicKey no longer points to it
  4. cache.setObject("two", forKey: cacheKey) retains K₂ for insertion into the cache

The end result is that the cache contains K₁ and K₂, and cacheKey points to K₂.


If instead of being an NSString, dicKey and cacheKey were NSMutableString, whose value can be modified at runtime without creating a new object, you'd see different behavior in the cache case:

let mutableDic = NSMutableDictionary()
var dicKey: NSMutableString = "key" // K₁
mutableDic.setObject("one", forKey: dicKey) // K₂
dicKey.setString("changedKey") // still K₁
mutableDic.setObject("two", forKey: dicKey) // K₃

print(mutableDic.object(forKey: "key") ?? "") // "one"
print(mutableDic.object(forKey: "changedKey") ?? "") // "two"

// BUT:

let cache = NSCache()
var cacheKey: NSMutableString = "key" // K₁
cache.setObject("one", forKey: cacheKey) // still K₁
cacheKey.setString("changedKey") // still K₁
cache.setObject("two", forKey: cacheKey) // still K₁!

print(cache.object(forKey: "key") ?? "") // "" !!!
print(cache.object(forKey: "changedKey") ?? "") // "two"


Related Topics



Leave a reply



Submit