tldr: ImagedNamed is fine. It handles memory well. Use it and stop worrying.
Edit Nov 2012: Note that this question dates from iOS 2.0! Image requirements and handling have moved on a lot since then. Retina makes images bigger and loading them slightly more complex. With the built in support for iPad and retina images, you should certainly use ImageNamed in your code. Now, for posterity's sake:
The sister thread on the Apple Dev Forums received some better traffic. Specifically Rincewind added some authority.
There are issues in iPhone OS 2.x where the imageNamed: cache would not be cleared, even after a memory warning. At the same time +imageNamed: has gotten a lot of use not for the cache, but for the convenience, which has probably magnified the problem more than it should have been.
whilst warning that
On the speed front, there is a general misunderstanding of what is going on. The biggest thing that +imageNamed: does is decode the image data from the source file, which almost always significantly inflates the data size (for example, a screen sized PNG file might consume a few dozen KBs when compressed, but consumes over half a MB decompressed - width * height * 4). By contrast +imageWithContentsOfFile: will decompress that image everytime the image data is needed. As you can imagine, if you only need the image data once, you've won nothing here, except to have a cached version of the image hanging around, and likely for longer than you need it. However, if you do have a large image that you need to redraw often, then there are alternatives, although the one I would recommend primarily is to avoid redrawing that large image :).
With respect to the general behavior of the cache, it does cache based on filename (so two instances of +imageNamed: with the same name should result in references to the same cached data) and the cache will grow dynamically as you request more images via +imageNamed:. On iPhone OS 2.x a bug prevents the cache from being shrunk when a memory warning is received.
and
My understanding is that the +imageNamed: cache should respect memory warnings on iPhone OS 3.0. Test it when you get a chance and report bugs if you find that this is not the case.
So, there you have it. imageNamed: will not smash your windows or murder your children. It's pretty simple but it is an optimisation tool. Sadly it is badly named and there is no equivaluent that is as easy to use - hence people overuse it and get upset when it simply does its job
I added a category to UIImage to fix that:
// header omitted
// Before you waste time editing this, please remember that a semi colon at the end of a method definition is valid and a matter of style.
+ (UIImage*)imageFromMainBundleFile:(NSString*)aFileName; {
NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
return [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/%@", bundlePath,aFileName]];
}
Rincewind also included some example code to build your own optimised version. I can't see it is worth the maintentace but here it is for completeness.
CGImageRef originalImage = uiImage.CGImage;
CFDataRef imageData = CGDataProviderCopyData(
CGImageGetDataProvider(originalImage));
CGDataProviderRef imageDataProvider = CGDataProviderCreateWithCFData(imageData);
CFRelease(imageData);
CGImageRef image = CGImageCreate(
CGImageGetWidth(originalImage),
CGImageGetHeight(originalImage),
CGImageGetBitsPerComponent(originalImage),
CGImageGetBitsPerPixel(originalImage),
CGImageGetBytesPerRow(originalImage),
CGImageGetColorSpace(originalImage),
CGImageGetBitmapInfo(originalImage),
imageDataProvider,
CGImageGetDecode(originalImage),
CGImageGetShouldInterpolate(originalImage),
CGImageGetRenderingIntent(originalImage));
CGDataProviderRelease(imageDataProvider);
UIImage *decompressedImage = [UIImage imageWithCGImage:image];
CGImageRelease(image);
The trade off with this code is that the decoded image uses more memory but rendering is faster.
I had this same question, and found some info in my searches (your question came up as one of the results). Here's what I determined...
There are two sides to the Cache-Control
header. One side is where it can be sent by the web server (aka. "origin server"). The other side is where it can be sent by the browser (aka. "user agent").
When sent by the origin server
I believe max-age=0
simply tells caches (and user agents) the response is stale from the get-go and so they SHOULD revalidate the response (eg. with the If-Not-Modified
header) before using a cached copy, whereas, no-cache
tells them they MUST revalidate before using a cached copy. From 14.9.1 What is Cacheable:
no-cache
...a cache MUST NOT use the response
to satisfy a subsequent request
without successful revalidation with
the origin server. This allows an
origin server to prevent caching even
by caches that have been configured to
return stale responses to client
requests.
In other words, caches may sometimes choose to use a stale response (although I believe they have to then add a Warning
header), but no-cache
says they're not allowed to use a stale response no matter what. Maybe you'd want the SHOULD-revalidate behavior when baseball stats are generated in a page, but you'd want the MUST-revalidate behavior when you've generated the response to an e-commerce purchase.
Although you're correct in your comment when you say no-cache
is not supposed to prevent storage, it might actually be another difference when using no-cache
. I came across a page, Cache Control Directives Demystified, that says (I can't vouch for its correctness):
In practice, IE and Firefox have
started treating the no-cache
directive as if it instructs the
browser not to even cache the page.
We started observing this behavior
about a year ago. We suspect that
this change was prompted by the
widespread (and incorrect) use of this
directive to prevent caching.
...
Notice that of late, "cache-control:
no-cache" has also started behaving
like the "no-store" directive.
As an aside, it appears to me that Cache-Control: max-age=0, must-revalidate
should basically mean the same thing as Cache-Control: no-cache
. So maybe that's a way to get the MUST-revalidate behavior of no-cache
, while avoiding the apparent migration of no-cache
to doing the same thing as no-store
(ie. no caching whatsoever)?
When sent by the user agent
I believe shahkalpesh's answer applies to the user agent side. You can also look at 13.2.6 Disambiguating Multiple Responses.
If a user agent sends a request with Cache-Control: max-age=0
(aka. "end-to-end revalidation"), then each cache along the way will revalidate its cache entry (eg. with the If-Not-Modified
header) all the way to the origin server. If the reply is then 304 (Not Modified), the cached entity can be used.
On the other hand, sending a request with Cache-Control: no-cache
(aka. "end-to-end reload") doesn't revalidate and the server MUST NOT use a cached copy when responding.
Best Answer
There is no way I know of to manually clear this iOS managed cache. In general, this is a red herring. When the os manages something for you, you don't need to worry about it. As long as you are correctly releasing anything you alloc/retain and handling memory warnings appropriately you're doing your part.