NSCache is not evicting data
Asked Answered
C

3

11

NSCache is a rarely used tool which actually looks quite useful. I created a simple experiment to see how it works and it looks like it does not auto-evict data in low memory situations (or I am doing something wrong!)

- (void)viewDidLoad
{
    _testCache = [[NSCache alloc] init];

    // Allocate 600 MB of zeros and save to NSCache
    NSMutableData* largeData = [[NSMutableData alloc] init];
    [largeData setLength:1024 * 1024 * 600]; 
    [_testCache setObject:largeData forKey:@"original_Data"];
}

- (IBAction)buttonWasTapped:(id)sender {

    // Allocate & save to cache 300 MB each time the button is pressed
    NSMutableData* largeData = [[NSMutableData alloc] init];
    [largeData setLength:1024 * 1024 * 300]; 
    static int count = 2;
    NSString* key = [NSString stringWithFormat:@"test_data_%d", count++];
    [_testCache setObject:largeData forKey:key];

    NSMutableData* dataRecoveredFromCache = [_testCache objectForKey:@"original_Data"];

    if (dataRecoveredFromCache) {
        NSLog(@"Original data is ok");
    } else {
        NSLog(@"Original data is missing (purged from cache)");
    }
}

So I ran the app in the simulator, and taped the button a few times however no items were evicted... The app eventually crashed:

2012-07-17 14:19:36.877 NSCacheTest[15302:f803] Data is ok
2012-07-17 14:19:37.365 NSCacheTest[15302:f803] Data is ok
2012-07-17 14:19:37.861 NSCacheTest[15302:f803] Data is ok
2012-07-17 14:19:38.341 NSCacheTest[15302:f803] Data is ok
2012-07-17 14:19:38.821 NSCacheTest[15302:f803] Data is ok
NSCacheTest(15302,0xac0942c0) malloc: *** mmap(size=393216000) failed (error code=12)
*** error: can't allocate region
Convenient answered 17/7, 2012 at 10:41 Comment(1)
I add a UIApplicationDidReceiveMemoryWarningNotification observer, and in that, empty the cache.Reider
N
16

From the doc (Emphasis mine): The NSCache class incorporates various auto-removal policies, which ensure that it does not use too much of the system’s memory. The system automatically carries out these policies if memory is needed by other applications. When invoked, these policies remove some items from the cache, minimizing its memory footprint.

Apple does not state that the memory will be freed on memory warning - in my experience, the cache is most often purged when the app goes to background or when you add more large elements.

Nickles answered 17/7, 2012 at 11:25 Comment(5)
Thank you for this. I found that simply putting the app into the background on the simulator resulted in Data is missing. However, I cant seam to get it to discard the memory even by continually allocating large blocks. Any ideas?Convenient
In fact locking or putting the app into the background causes the items to be evicted no matter how small the data. However continually allocating 50 mb chunks and storing them in the cache does not free them... Eventually the app just crashes!Convenient
Interesting, i have not observed such behavior - are you testing on simulator or on a device?Nickles
I actually did, both... I have adjusted my question with the source code I used. (although I used smaller allocations on the device)Convenient
@Convenient You can associate “cost” with each cached object and set cost limit on the NSCache object. Let’s say, you set the cost to the data.length, so you can limit allocated memory.Houle
S
7

Here's quoted docs ...

The NSCache class incorporates various auto-removal policies, which ensure that it does not use too much of the system’s memory. The system automatically carries out these policies if memory is needed by other applications. When invoked, these policies remove some items from the cache, minimizing its memory footprint.

... as you can see it states that it removes some items, not all items. It depends on NSCache internal policies, available memory, device status, etc. You shouldn't worry about these policies.

You can control them with countLimit, totalCostLimit properties and you can add object with cost, look at setObject:forKey:cost:.

Also you can evict objects by yourself. Add NSDiscardableContent protocol implementation to your objects and setEvictsObjectsWithDiscardedContent: to YES.

Sparkie answered 17/7, 2012 at 11:21 Comment(1)
What I wanted to say is that even if you emit Memory Warning in simulator, NSCache can still think (= know) that there's lot of free memory and does nothing.Sparkie
F
1

I am using that class too. Note that the documentation states the NSCache is tied into the OS and probably has access to memory information deep within the OS. The memory warning is just that - it just sends a memory warning to the appDelegate/viewControllers.

If you really want to test your code out, you will probably need a test mode where you start mallocing lots of memory (creating a huge leak so to speak). You might need parcel this out in chunks during each main runloop, so the OS has the opportunity to see he memory going down (I have an app that chew ups lots of memory, and it does it so fast on the 3GS it just gets killed never having got a memory warning.

Fortnightly answered 17/7, 2012 at 11:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.