NSCache removes all its data when app goes to background State
Asked Answered
A

1

8

I am working with NSCache, I am using NSCache to store images. I am showing images on UITableView. Whenever I add images first they are resized and then added to the table and then to NSCache. eveything works fine.

But whenever app goes to background when I close the app and open again, My Cache will be empty and again my app resizes images and then show it, because of which at first I see a empty table.

I am not understanding why this is happening. Is this the intended behaviour of NSCache? .If yes then how can we improve the user experience so that use doesnt see the lag.

here is my code which was suggested to me by @ipmcc

Here category is my entity name (I am using coreData)

// A shared (i.e. global, but scoped to this function) cache
    static NSCache* imageCache = nil;
    // The following initializes the cache once, and only once
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        imageCache = [[NSCache alloc] init];
    });

    // Generate a cache key sufficient to uniquely identify the image we're looking for
    NSString* cacheKey = [NSString stringWithFormat: @"%@", category.name];
    // Try fetching any existing image for that key from the cache.
    UIImage* img = [imageCache objectForKey: cacheKey];
    self.imageView.image = img;

    // If we don't find a pre-existing one, create one
    if (!img)
    {
        // Your original code for creating a resized image...
        UIImage *image1 = [UIImage imageWithData:category.noteImage];

        CGSize newSize;
        if(image1.size.width == 1080 && image1.size.height == 400)
        {
            newSize = CGSizeMake(300, 111);
        }

        dispatch_async(dispatch_get_global_queue(0,0), ^{
            UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
            [image1 drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
            UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();

            dispatch_async(dispatch_get_main_queue(), ^{
                // Now add the newly-created image to the cache
                [imageCache setObject: newImage forKey: cacheKey];
                self.imageView.image = newImage;
            });
        });
    }
Afterimage answered 16/12, 2013 at 8:15 Comment(11)
How are you closing the app and how have you implemented the cache usage (show code).Gauzy
hi @Wain, I have added code in my above question. I am closing the app by pressing the home button.Afterimage
Yes, this is the intended behavior of NSCache. If you intend to circumvent it by using NSDictionary you will likely not have an app to come back to at all since you app will be killed in the background due to high memory usage. Don't fight this process. Instead have a smooth transition into your images if they are not available. There are libraries out there for downloading and caching images. NSCache should not be used for images.Hamid
hi @borrrden, I am not downloading my images from any server, My images are stored in coreData db , I am just fetching those images from it , resizing it and then displaying it on the table.IF NSCache should not be used for images, then what should be used?Afterimage
Why are your images stored there? That is a weird way to do things...instead you should consider storing the image NAME and then you can use imageNamed: and get the caching behavior for free.Hamid
@borrrden, sorry I meant my entity called Category is stored in db, which has an attribute called image. And I didnt get your solution of just saving Image name and getting caching behaviour for free.Afterimage
The normal way to do it would be to save the filename of your image in the DB, and then go to get it with [UIImage imageNamed:]. This method will cache the image for you so that the next time you get it, it is from memory instead of disk. It will automatically purge under pressure as it should.Hamid
@borrrden, The images are created by user, user creates these images on fly. Infact I am converting whatever user draws into a image.Afterimage
let us continue this discussion in chatAfterimage
In that case, I suggest trying this library -> github.com/path/FastImageCacheHamid
@borrrden, I will look at it and get back to youAfterimage
B
17

Yes it does that, for some reason it instantly removes data from cache when app enters background even if there is no memory pressure. To fix this you have to tell NSCache that your data should not be discarded.

Here is how you fix this.

class ImageCache: NSObject , NSDiscardableContent {

    public var image: UIImage!

    func beginContentAccess() -> Bool {
        return true
    }

    func endContentAccess() {

    }

    func discardContentIfPossible() {

    }

    func isContentDiscarded() -> Bool {
        return false
    }
}

and then use this class in NSCache like this.

let cache = NSCache<NSString, ImageCache>()

and then set the data in cache like this

let cacheImage = ImageCache()
cacheImage.image = imageDownloaded
self.cache.setObject(cacheImage, forKey: "somekey" as NSString)

and to retrive the data

if let cachedVersion = cache.object(forKey: "somekey") {
     youImageView.image = cachedVersion.image
}
Bowra answered 10/9, 2018 at 13:35 Comment(2)
This helped me immensely when populating collectionViews. NSCache also removes items if they are within a reusable cell that is no longer within the visible window. With this new NSObject and a good extension on UIImageView the collection view loads a lot smoother now.Horizontal
I have been looking at NSDiscardableContent definition, and I can't seem to understand why this works :-) Do you have some explanation why this works?Handmaiden

© 2022 - 2024 — McMap. All rights reserved.