Force to use memory cache not working: cacheType is 'none'
Hi everyone,
Kingfisher is an awesome library, but I can't seem to implement the following:
In order to resolve a slow/jittery UICollectionView on initial scrolling, I checked out Time Profiler and I believe the jitter comes from UIImageView.kf.setImage() which would make sense. I am already using the kingfisher prefetch method with .cacheMemoryOnly to prefetch the images used in the cells of my collectionview long before actually reaching the view where the uicollectionview resides. Then when I populate the cells I use UIImageView.kf.setImage() with the .fromMemoryCacheOrRefresh option, since I believe the lag is caused because it is loading the image from Disk and not fro Memory.
However! when I print out the value.cacheType in the setImage completionHandler, the first results are "none" not "memory" as expected. It is only after scrolling up and down a couple of times that the cache type indeed becomes "memory".
So my question is the folloying:
- am I correct in assuming that loading my cell's images from memory instead of disk will improve performance of my UICollectionView?
- if so how can I force KingFisher to not use Disk or Network-fetching, but simply prefetch all the images in memory and load them from memory when the time comes. I simply cannot find the correct settings to achieve this behaviour.
Thank you!
-
.cacheMemoryOnlymeans Kingfisher will skip caching the image to disk. -
.fromMemoryCacheOrRefreshmeans Kingfisher will try to find an image from memory cache first. If not matched, it will skip searching from disk cache, but download the image again. It is a trade-off. - The memory is limited on a device. Kingfisher restricts itself to use memory with an upper limitation. If you cache a lot of images in memory, old or unused ones will be purged.
So your experience above is not strange. You indeed downloaded the images and cached them in memory, but you didn't cache them to disk. Later, when you want to show them in a collection view, these images are removed from the memory cache, so you need to download them again, which brings an awful experience.
Now, for your question.
- Yes, loading cell's images from memory instead of the disk will improve your time performance. But the cost is more memory usage (you can set a larger number to Kingfisher's memory cache to allow more images to be cached).
- I personally suggest you can remove these options (
.cacheMemoryOnlyand.fromMemoryCacheOrRefresh), just use the default settings. It is designed carefully to get a balance between memory and timing. Furthermore, even loading images from the disk cache will not block your main thread. So your Time Profiler should show Kingfisher's image loading time consuming mainly happens in a background thread. It will not block your UI at all, so the timing should be just fine in most cases.
IMO, it is fine to use ImagePrefetcher to fetch images before where they are really needed. However, it is originally designed for table view and collection view data prefetching. So I suggest just use it in a UICollectionViewDataSourcePrefetching delegate to download only several images before they are about to be shown. This type is NOT DESIGNED as a good general-purpose image downloader for thousands of images (although you are not forbidden to do so).
You indeed downloaded the images and cached them in memory, but you didn't cache them to disk. Later, when you want to show them in a collection view, these images are removed from the memory cache, so you need to download them again, which brings an awful experience.
But why? I just downloaded them and forced them to be cached in memory, why do I need to download them again? How does Kingfisher deside when the 'unused' images should be redownloaded? I prefetch them the first time I arrive at the scroll-view, once the user scrolls to the second view in the scrollview the uicollectionview appears... why does it print out that the images are loaded from cahce type 'none' instead of 'memory'. It just doesn't make sense to me :(
But thnx for the explanation :)
Hi,
Yes, I can understand your confusion. But as I mentioned, there are some limitations on the memory cache. If these conditions are met, the underlying NSCache will purge the cached objects and release them.
By setting .cacheMemoryOnly, you say you want to only use memory, but to skip the disk cache. It does not mean "forced them to be cached in memory". All loaded images will be cached to memory until the cache limitation has been reached.
- You skipped the disk cache by
.cacheMemoryOnly. - The cached images in memory have been purged due to limitations reached.
- Then you want to load these images back, Kingfisher cannot find them in the cache.
- So it triggers a redownload.
That's the whole story.
Please read the doc to know more about .cacheMemoryOnly and .fromMemoryCacheOrRefresh. Again, I suggest you just use the default settings or follow examples in the cheat sheet as the first step with Kingfisher until getting more details.
Best regards.
I totally understand now. thanks for the explanation!
As I was getting redownload behaviour pretty early after downloading 20-50 images each of around 50-200 KB in size, maybe using a custom cache with more memory space, would make it work?
If I change the cells to use a static asset image using 'UIImage(named: "") in the 'cellforitem'-method, I do not have the laggy behaviour. If my images are cached in memory, should I get the exact same smooth behaviour as using UIImage(named:...) ?
Thank you, and great work with the library 👍
As I was getting redownload behaviour pretty early after downloading 20-50 images each of around 50-200 KB in size, maybe using a custom cache with more memory space, would make it work?
Yes, it should work better for your case. Instead of the encoded size (50-200KB) in disk, the pixel bitmap data (the height and width of the image) in memory is more important to determine the memory usage. If you are loading some big images, the memory purge could happen easily. If the image is much larger that what you need (the size of your image view), I suggest you can apply a DownsamplingImageProcessor when setting image (like this).
If my images are cached in memory, should I get the exact same smooth behaviour as using UIImage(named:...)?
Yes.