My iOS app is somewhat similar to Facebook in the sense that it will display user generated 'posts' in a scrollable tableview, each with an image to display.
To try and avoid downloading images each time the users scrolls through the tableview, I'm using FTWCache to store each post's image data, as such:
[FTWCache setObject:loadedData forKey:[NSString stringWithFormat:#"postID%#", post.ID]];
Is this a poor practice, to lean heavily on a caching class like this? After awhile, the cache would become considerable in size, should I be flushing it every so often?
If anyone has any advice it would be much appreciated.
I'm not familiar with FTWCache but I can see that it was last maintained 3 years ago while SDWebImage or AFNetworking is maintained daily with a really big community behind it.
I'd go with AFNetworking. You shouldn't worry too much about leaning heavily on caching. That's what it's for. You could however read a bit more about how NSURLCache works and how AFNetworking is using it.
Here's the gist of it:
AFNetworking takes advantage of the caching functionality already
provided by NSURLCache and any of its subclasses. So long as your
NSURLRequest objects have the correct cache policy, and your server
response contains a valid Cache-Control header, responses will be
automatically cached for subsequent requests.
Related
I use alamofireimage for loading images async and I use AutoPurgingImageCache for caching the loaded images (url request). I have a pull-to-refresh feature in my app and after I do that even if the app content remains the same I see that the app size is increasing and alamofire does not fetch the images from cache and instead it loads them from new requests. Profiling the app reveals that a memory issue exists in alamofire. I have attached the screen shot. I would appreciate if someone has some insight into this.
What cache size did you specify? It's possible that you're experiencing cache misses because the number of distinct images you request per-refresh is more than large enough to fill up the entire cache.
It's also possible that the cache key, i.e. image URL, is changing between refreshes, which could prevent your caching mechanism from correctly looking up cached images.
It's certainly possible there's a problem with AlamoFire, but you should probably start by examining your caching setup for problems like the above.
I am fetching images from my server and need to store them on the disk. I was wondering what is the best way to cache these images to prevent excessive requests and traffic. From doing some research it seems that there are many options available but I am not sure which to use.
Core Data
Store in the Cache Resources Folder in the file directory
After storing these is it best to use a NSCache class to put these data into memory for quick access or is Core Data quick enough?
Based on my experience, you could use SDWebImage, which caches the images you request based on their url, so the next time you "request" it, it will check if it is in cache, if so it won't make the request and it will load it from it instead.
I'm not sure why would you need to store the image, maybe you could tell us the reason and see if we can help any further.
I've had great results using FastImageCache by Path.
What Fast Image Cache Does
Stores images of similar sizes and styles together
Persists image data to disk
Returns images to the user significantly faster than traditional methods
Automatically manages cache expiry based on recency of usage
Utilizes a model-based approach for storing and retrieving images
Allows images to be processed on a per-model basis before being stored into the cache
I'm downloading images using [SDWebImageDownloader.sharedDownloader downloadImageWithURL] with options set to 0. I'm initially not doing anything with them, with the understanding that they will be cached. However, when I use the exact same function to later display an image, the function is downloading the image again, rather than getting it from the cache (the image cache type is 0). In both cases, the url of the image is the same. Is my understanding regarding caching incorrect?
The easiest way to enjoy cache functionality is to use SDWebImageManager instead of SDWebImageDownload. SDWebImageManager provides the SDImageCache functionality, whereas if you use SDWebImageDownload, you'll have to rely upon NSURLCache (which has limitations/issues) or write your own cache code.
Also (and implicit in Gustavo's question), if you're just trying to set the image of a UIImageView, it's actually even better to not use either of those classes, and use the UIImageView+WebCache category instead. It enjoys all of the cache abilities of SDWebImageManager, but also offers other advantages (esp for re-used UITableViewCell and UICollectionViewCell objects).
In a comment to another user, you say that you're downloading all of the images in advanced, "just to get them cached, so that when the user actually does want to see an image he doesn't have to wait."
That is a great stretch objective, but this sort of prefetch (sometimes call eager loading, in contrast to the more common lazy loading) has a couple of implications:
Unless you're confident that the user really will need all of the images, this is an aggressive use of their mobile device's cellular data plan, so maybe you should only do this if on WiFi (which can be determined by Reachability). Apple has even rejected apps for using too much cellular bandwidth.
The app will be more aggressive than necessary in terms of memory (causing more suspended apps to be terminated, which doesn't affect the UX for your app, but Apple asks us all to be good citizens and not use more RAM than we need). Again, if the user was going to need all of the images, then it's a fine thing to do, but if not, one should really minimize memory consumption, not loading the cache up with stuff that might not be needed for the current session. Also note that downloading a bunch of stuff that might need to be downloaded, but was done simply as a precaution has (modest) battery implications, too.
If you do a lot of requests for background data, make sure you're not using up all of the limited the network connections (you only have five) and backlogging the system with a lot of requests. The nice thing is that the UI UIImageView category naturally favors the current UI (being, fundamentally, a lazy-loading mechanism). But let's say there are 100 images, and the user fires the app and scrolls down to the bottom of the list. Do you really want the request for #90 (which is on screen and the user is waiting for) to wait for #1-89 to finish?
See WWDC 2012 video Asynchronous Design Patterns with Blocks, GCD, and XPC, section 7, "Separate control and data flow", about 48 min into the video for a discussion of how this is problematic.
If nothing else, I'd make sure that you test the app using the network link conditioner (part of the hardware IO tool for MacOS or under the Settings > General > Developer on the device). So turn on the network link conditioner, remove and reinstall the app (to empty the persistent storage cache), and then fire up the app with this slow connection, try navigating around while the image loading is in progress. A simple "let's kick off a prefetch of everything" may not offer the necessary prioritization of the current UI on a slow network that you really want.
All of this said, you may have thought through all of these implications, and if so, I apologize for belaboring the obvious. It's just that one has to be careful before implementing an aggressive pre-fetch of all images.
I read something about multi thread access on DB but still not sure best way to do that for reading/writing in conjunction with async network download.
For instance I will have a page with images from the web, so I'm retrieving them by their URLs, using AFNetworking but I want to check first on my DB and write on it (or disk) the retrieved image for future use.
What can be the best way to do that without blocking the UI (like when scrolling)?
If I do that with a singleton that reads/writes it block the main thread.
Thanks for any tips on that.
AFNetworking is not the tool for that. Instead, you can take advantage of the built-in functionality of NSURLCache--specifically Peter Steinberger's fork of SDURLCache.
In -applicationDidFinishLaunchingWithOptions:, do NSURLCache +setSharedCache: with an instance of SDURLCache (with some amount of disk space allocated). All requests made through UIWebView (and AFNetworking, for that matter) will automatically be routed through NSURLCache before the request is made to check the cache. It's unobtrusive, drop-in, and follows cache directives correctly, and it should solve your problem quite nicely.
I have been neglecting learning about caching for quite some time now, and although I've used caching here and there in the past it's not something I'm familiar with.
I found a great tutorial about what caching is and what kinds of cache there are (I know already what caching is), but...
How does one decide what and when to cache? Are there things that should always be cached? On what situations should you never use caching?
First rule is: Don't cache until you need it, that would be premature optimization (first link I found, google for more info)
The biggest problem with caching is invalidation of cache. What happens when the data you have cached is being updated. You need to make sure your cache is updated as well and if not done correctly often becomes a mess.
I would:
Build the application without
caching and make sure the
functionality works as intended
Do some performance testing, and
apply caching when needed
After applying caching do
performance testing again to check
that you are getting the expected speed increase
I think the easiest way is to ask yourself a bunch of questions,
Is this result ever going to change?
No? then cache it permanently
Yes, When is it going to change? When a user updates something.
Is it going to impact only the particular user who changed the value or all of the users. This should give you an indication of when to clear the particular cache.
You can keep on going, but after awhile you will end up with different profiles
UserCache, GlobalCache just being 2 examples.
These profiles should be able to tell you what to cache and have a certain update criteria (When to refresh the cache)