Changing NSURLCache memory and disk capacity doesn't work - ios

I'm using a NSURLSession configured to use the shared NSURLCache. I'm trying to change the size of the cache when the app goes in and out of background. The idea is to reduce both memory and disk capacity when the app enters background and restore the original capacity when the app enters foreground.
For both memoryCapacity and diskCapacity properties of NSURLCache the doc says :
At the time this call is made, the [in-memory|on-disk] cache will truncate its
contents to the size given, if necessary.
However, when I set these properties, the cache size never changes even if I set a value (much) lower than currentMemoryUsage or currentDiskUsage. Also I can easily verify that the memory used by the app doesn't change, an the size of the disk cache doesn't change either.
It seems that NSURLCache only use the values set when it is initialized and doesn't take into account subsequent changes. Calling the getters for memoryCapacity and diskCapacity does return my new values though.
Am I missing something or doing something wrong or is this a bug of NSURLCache ?

Probably an error in the documentation.

Related

iOS: Realm file is size going exponentially more than GBs

I am using Realm as persist store for swift based iOS application.
I have four tables with 6 columns each and each table is having 5k entries each table.(all are strings).This structure is reaching to 3GB.
what could be the issue it reaches 3GB?
First of all, please make sure that the data is actually 3 Gbytes or not. If it contains a lot of transaction version data, the file size will increase from the actual size. To do this, execute compaction or copy the Realm file.
Please see more details:
https://realm.io/docs/swift/latest/#limitations-file-size
​
At that time, if the file size does not change, 3 Gbytes of data is actually stored, so it can not be made smaller.
If the file size decreases, the file size is larger than the actual due to the transaction version pinning.
When I look at your code, you are using Realm in multithread, so there is a possibility to occur unintentional version pinning. Try to use autorelease pool to minimize the lifetime of Realm instance. Also, setting shouldComplatOnLaunch to periodically compact at startup can also help solve the problem.

When exactly do things get removed from urlcache's memory and disk?

let memoryCapacity = 200 * 1024 * 1024
let diskCapacity = 1 * 1024 * 1024
let cache = URLCache(memoryCapacity: memoryCapacity, diskCapacity: diskCapacity, diskPath: "myDataPath")
URLCache.shared = cache
Scenario1
I'm setting urlcache's memory to 200mb and setting the disk space to 1mb. Then I download an image. Turn off internet and force quit the app, launch the app again by tapping it, and trigger a call (to download image) but it immediately crashes because data is nil
Needless to say if I turn off internet, it will still read from cache as expected and no crash would happen.
Scenario2
If I set the diskCapacity to 200mb (let diskCapacity = 200 * 1024 * 1024)
then even though the cache is flushed out of memory, still the image is persisted in the disk; it will always show!
Based on this observation, I have the following questions:
Is it correct to say, every time you force quit or your app crashes due to a memory warning/issue your cache will get flushed, though it leaves your disk intact?
Any other reason where cache can get flushed out of memory?
When can things stored in disk get removed?
Short answer:
The memory cache is lost when the app terminates (or, likely, under memory pressure, too). The disk cache persists across multiple invocations of the app, though can be removed if the device runs out of persistent storage and the OS reclaims space from caches and temp folders.
Long answer:
You ask:
Is it correct to say, every time you force quit or your app crashes due to a memory warning/issue your cache will get flushed, though it leaves your disk intact?
Largely. It may be more precise to say simply that all memory related to your app is discarded when the app terminates and that only those items saved to persistent storage can be retrieved when the app restarts.
Any other place where cache can get flushed out of memory?
You lose everything in the memory cache when the app terminates. There are obviously a few other situations in which case items can be removed:
If you manually remove responses from the URLCache.
Older, individual items are removed from the cache as you approach the max capacity of the cache and you try to add new items, forcing older items out.
Network responses often include a cache policy (indicating how long the response can be safely cached), so it's likely that they're removed at that point.
The memory cache may reasonably be purged upon memory pressure (e.g. .UIApplicationDidReceiveMemoryWarning).
When can things stored in disk get removed?
The logic is largely the same as prior point, except that (a) it can survive across invocations of the app); and (b) it's not flushed upon memory pressure, though it can be removed when the device runs low on persistent storage.

Does iOS cache images by default in memory? (RAM)

I've been struggling to find out why my application continues to increase in memory as I move throughout the application.
When leaving the view, I make sure to check to see if the controller is de-initialized and it is, but the memory that was added while in the view retains. I've used the instruments tool and it hasn't detected any leaks, and leaving/re-entering the view repeatedly doesn't have any effect on the used memory.
This leads me to believe that iOS by default caches the UIImage into memory, and only frees the memory if the device needs it.
The view that I'm working with is a UICollectionView which shows the user a gallery of pictures that have been uploaded to my server. Currently I have this limited at 10 images per user, but as you can imagine if there's quite a few images and that can increase the memory rather quickly.
Do I need to worry about this memory? Is it default behaviour for the images to stay in memory until the device needs to free some space? I don't want to submit to the application store and get rejected for poor memory-management.
EDIT: It's also fair to note that I am constructing the image using the UIImage(data: NSData) constructor.
iOS does natively cache plenty of memory. The underlying support for that is in libcache, which UIKit uses internally, in a way that is inaccessible to you.
During times of "memory pressure", that is, when RAM is low, an event (technically , a knote) is broadcast to all listeners. Your app is listening because the frameworks automatically open a kevent file descriptor, and react to it with the well known didReceiveLowMemoryWarning.
Remember, there's no swap in iOS (discounting compressed RAM for the scope of this answer), so this happens quite frequently.
Even before didReceiveLowMemoryWarning is passed to you, libcache uses the malloc feature of "zone pressure relief" , which you can see for yourself in :
...
/* Empty out caches in the face of memory pressure. The callback may be NULL. Present in version >=
8. */
size_t (*pressure_relief)(struct _malloc_zone_t *zone, size_t goal);
} malloc_zone_t;
Thus, images (main consumers of memory) will be purged if necessary, and therefore should be of much concern to you. That said, if you know you don't want a given UI object anymore, you can of course dispose of it explicitly. Of course, if you have any additional resources that cannot be auto-purged in this way, you should handle them in the delegate. Because if you don't, Jetsam will jettison your app (i.e. kill you with an untrappable -9), and probably slay a few innocents in your priority band as well.

What happens if NSURLCache is full?

I'm using default NSURLCache to cache images in my iPhone app.
What will happen if the cache is full and i'll try to cache another image?
Will it not cache the image? or it will be replaced with the oldest image cached?
Thanks alot
The maximum cache can be influenced with the initialization
initWithMemoryCapacity:... diskCapacity:... diskPath:..]
The new file will always be downloaded. (except when it's bigger than the maximum memory capacity, then it will just be downloaded and not saved in the cache)
If the maximum cache size is reached other file(s) will be removed.
It is not specified what files will be removed.
It's not difficult to create your own NSUrlCache and handle it yourself.
if you want to see a sample how to do that, then have a look at https://github.com/evermeer/EVURLCache

In-Memory cache and DiskCache for Images Strategies

Now, I am developing a news reader app like BBC news iOS.
see in BBC News
In my app, I must download image from server to and show it in view to make users easier to choose the news they want to read.
For more performance, I must cache image to avoid reloading image for server.
I know that there are 2 kinds of cache: In-memory cache that saving images in memory (RAM) and DiskCach that save images in Disk to load it when we need.
My question is:
What is best images cache mixed strategies for my App? (use both in-memory cache and image-cache)
My solution is:
download image --> save them in diskcache + save them in memory cache --> load image from in-memory cache on demand and show in view ---> in-memory cache over its MAX_SIZE --> free in-memory cache ---> load image from disk cache on demand and save it to memory cache --> repeat........
Is my solution is right approach?
Another question: when in-memory cache over its MAX_SIZE --> we will free its --> all images in cache will lose so image in our view will disappear.
--> How to solve this problem?
Sorry for poor English.
Thank in advance.
In one of my projects I implemented pretty much the same caching methods (Disk Cache and Memory Cache).
Maximum cache size
Each cache system had its own max size limit. The "size" of each image was computed differently in the cache systems.
For the memory cache, each image would have a size computed as
image size = image width * image height (in pixels)
So, the maximum size for the memory cache would represent a the maximum area of a pixel surface
For the disk cache, I used the actual file size for each file.
Making room
When using the cache systems, you might get to a situation where one of the caches is full and you want to insert a new item in it - you have to remove some items to make room.
What I did was assign a timestamp to each entry in the cache. Every time I access that item I updated the timestamp. When you want to make room, you just need to start removing items from the oldest to the newest based on the last access timestamp.
This is a simple algorithm for freeing up space and in some cases might actually behave poorly. It is up to you to experiment and see if you need something more advanced than this.
For example, you could improve this method by adding a priority value for each item and keep old items in the cache if their priority is high.
Again, it depends on your app's needs.
Expiration
For the disk cache, I would definitely add an expiration date for each entry. If the memory cache is destroyed when the user completely terminates the app, images in the disk cache might be stuck in there forever.
Encapsulation
Another aspect I would consider, is making the caching system as transparent as possible to the programmer. If you want to enable/disable one of the cache it would be best to have most of the code remain the same.
In my app, I built a central content delivery system and I would always request images from the internet through this object. The caching system would then check the local caches (memory / disk) and either return me the image immediately or make a request to download it.
Either way... I, as the "user" of the caching system did not care what was happening behind the curtains. All I knew is I made a request to get an image from an URL and I got it (faster or slower depending if the image was cached).

Resources