Dispelling the UIImage imageNamed: FUD - iphone

Edit Feb 2014: 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.
I see a lot of people saying imageNamed is bad but equal numbers of people saying the performance is good - especially when rendering UITableViews. See this SO question for example or this article on iPhoneDeveloperTips.com
UIImage's imageNamed method used to leak so it was best avoided but has been fixed in recent releases. I'd like to understand the caching algorithm better in order to make a reasoned decision about where I can trust the system to cache my images and where I need to go the extra mile and do it myself. My current basic understanding is that it's a simple NSMutableDictionary of UIImages referenced by filename. It gets bigger and when memory runs out it gets a lot smaller.
For example, does anyone know for sure that the image cache behind imageNamed does not respond to didReceiveMemoryWarning? It seems unlikely that Apple would not do this.
If you have any insight into the caching algorithm, please post it here.

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.

In my experience, the image cache created by imageNamed does not respond to memory warnings. I've had two applications that were as lean as I could get them as far as mem management, but were still inexplicably crashing due to lack of mem. When I stopped using imageNamed to load the images, both applications became dramatically more stable.
I will admit that both applications loaded somewhat large images, but nothing that would be totally out of the ordinary. In the first application, I just skipped caching altogether because it was unlikely a user would come back to the same image twice. In the second, I built a really simple caching class doing just what you mentioned - keeping UIImages in an NSMutableDictionary and then flushing its contents if I received a memory warning. If imageNamed: were to cache like that, then I shouldn't have seen any performance upgrade. All of this was running on 2.2 - I don't know if there's any 3.0 implications on this.
You can find my other question around this issue from my first app here:
StackOverflow question about UIImage cacheing
One other note - InterfaceBuilder uses imageNamed under the covers. Something to keep in mind if you do run into this problem.

Related

iOS4 calling ImageNamed: still leak or cause memory issue?

So apparently, my app crashes on ipod 2nd generation due to low memory issue. What I do was calling image on each view within scrollView + pageControl when user scrolls. And app crashed when it reached a particular point after got memory warning. I tried to free up view when I got warning but it still caused crash.
I googled about ImageNamed: and apparently there was issue within this api call, but most article said it was fixed in recent iOS version.
I fixed this problem with calling image imageWithContentOfFile instead imageNamed, but I'm wondering if ImageNamed still causes memory leak or not free up when it view is released.
imageNamed: doesn't cause a leak, but it is frequently misunderstood which is what leads to memory issues when it's used. It caches the uncompressed image after it is loaded, which means there are immediately 2 copies of that image in memory. If you use it for small, frequently used images (such as icons), this is great because the runtime doesn't have to fetch the file off disk - it's already available in the cache. Where this gets users into trouble is when they use imageNamed: to load a large image, say a 4MP image taken with a camera. That image takes up quite a bit of memory: 4 million pixels, types 4 bytes per pixel = 16MB of memory, TWICE. If you use that method to load images for your slideshow, photo sharing, camera app, or whatever, it adds up real fast.
So if those features don't fit what you need, use one of the other UIImage loading methods. You users will thank you.
Note: This information comes from the Apple Engineer which presented the UIKit rendering session (#121 I think it was). Hopefully my notes are correct :)

When is it worthwhile to cache UIImage resources?

I've been doing a lot of iPhone UI work with image files that are used in multiple locations in a single view or in several views throughout the application. In some cases, I'm drawing new icons, usually by compositing 2 small images (each less than 4 KB).
I've thought a bit about optimizing the loading of images, but I'm not sure what the best practices would be. I would guess that it would be worthwhile to save any images that are created or altered using CG functions. With images that aren't altered, what is the overhead of loading images from a bundle?
UIImage* image = [UIImage imageNamed:#"myImage.png"]
With the memory constraints of a mobile device in mind, what factors are most important when considering caching images? The size of the image, the total number of images that may be cached, and the number of times a single image is loaded come to mind.
In the latest performance sessions at WWDC (2011), Apple didn't recommend caching images for most cases. They recommend that you only cache images when you know for a fact, after a performance analysis, that you need to cache images ahead of time because you can't afford the time to load them off disk and decode them. In most cases you probably can afford it.
They specifically noted, as #Till does, that +[UIImage imageNamed:] caches images for the lifetime of your process, and so they recommend using a non-caching loading method, such as +[UIImage imageWithContentsOfFile:]
The reason is that memory is a constrained resource on iOS devices, so if you cache your images, you are likely to cause memory pressure on the system, and apps to get jetsammed. And since iOS 5 jetsams apps using more memory first, if you're caching a bunch of UIImages you're going to make it more likely for your app to get jetsammed.

Does UIImage's imageNamed still cause memory issues on iOS4 for large images?

I found a couple of Memory Management articles that mentioned UIImage's imageNamed causing problems when you have a lot of large images in memory.
http://akosma.com/2009/01/28/10-iphone-memory-management-tips/
http://www.alexcurylo.com/blog/2009/01/13/imagenamed-is-evil/
Both of these were written for OS version <= 3.0.
Does anyone know if these are still a problem in iOS 4?
No longer a problem, see here, and maybe here.
If you look at this link: Dispelling the UIImage imageNamed: FUD you'll see there are really two problems with large images and imageNamed:
Prior to iOS 3.0 was a bug in the caching routines of imageNamed: such that it would not let go of its cache even when it received a mem warning. This was a pretty major issue and was the source of a lot of memory crashes for me personally, as there's no way you could get memory back after using imageNamed: to load it. This is NOT an issue anymore, as this seems to have been fixed in 3.0.
imageNamed: keeps, in its cache, the uncompressed image data itself. For really large (in terms of their size on screen - width x height) images, this data can be really large. width * height * 4 is what I've commonly seen cited for its uncompressed size. This would still potentially be a problem for you if you are using rather large images - and is especially to be avoided if you don't need to redraw them often.
So to recap: 1 is no longer a problem, 2 might be.
it does.
In combination with UIPageViewController, definitely it does.
trying to build a kids application displaying animal's sound and cartoon keyFrame animation
all individual animal details are in their own UIViewController
All views are displayed in UIPageViewController (Transition:scroll).
Didn't know about imageNamed bug and for nearly a month , thought it was a problem caused by UIPageViewController. Memory was never being released by ARC.
ASA i switched to imageWithContentsOfFile instead of imageNamed, all problems solved.
UIPageViewController was innocent in real. It works smooth now. No unreleased memory problem.

Memory [ Real RAM ] used by App on IPhone 4.0 on lauch and then using UIImageview

I have two queries
I was running a sample app which having single view controller in it implemented. When I check the memory using Instrument it was showing 3.66 MB
. Wondered why it is taking so much of RAM as there is nothing much heavy in app.
When I have added UIImageview with the Image having size of 25 KB,then Memory uses go to 4.24 MB
[ I come to know the reason behind is "image is unpacked 320*480*4 = 580 KB" but need to debug more on this & it remains in cache ]
Along this I have also observed two scenarios
When we uses api [UIImage imageNamed:aName] for loading image, then calling [UIImageview release] doesn't have any effect.
But When we use
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:aName ofType:nil]];
Some memory is cleaned up when I call [UIImageview release]
In my app , I am going to use lot of images and that will cause a low memory and will crash the app.
Please provide me some ref or path to analyse why this behavior.
Thanks,
Sagar
Trying to fit your app in memory is a losing game. It'll lead you down weird paths of trying to figure out which sort of device you're running on, swapping in and out resources based on that, etc, etc.
The better option is to design your memory structure for ditchability, and then support a fairly harsh ditching regimen when you're notified of low memory conditions. Go ahead and use memory--it's there for that--and let the low memory warning be your signal to prune out unused resources.
A lot of people seem to feel bad that their application generates memory warnings. That's silly. The design pattern here is, eat all you want, but respond appropriately when you're told you're overweight. Given you're running on a broad range of devices with a broad range of memory profiles (an iPhone 3G has 1/4 the RAM of an iPhone 4 for instance), the best way is just to be sensitive to when you've filled memory.
The main hurdle you'll encounter is recovering from having ditched data. I find what works best is to explicitly set UIImage objects to nil, and then test for nil before using them, reloading them from the bundle or the network or whatever if necessary.
All that said: [UIImage imageNamed:] supports ditchability, but you don't have control over it. When a UIViewController subclass gets a memory warning, it will ditch cached UIImages that you've created with that method, but nothing you can do will make them go away until then. Even assigning a tiny something to the UIImage in question won't help because it's cached associated with the "name" that it's "Named", not the object that it is assigned to. So that method is good for images you're going to reuse a lot, but even so, it will get pruned when the time comes, and you need to respond appropriately.
Images, loaded with imageNamed, are cached in memory by UIKit, and images, loaded with imageWithContentsOfFile are not.

UIImagePickerController and the need to save the UIImage asap vs. exploiting the file backing

I've read uiimagepickercontroller uiimage memory and more and other relevant questions, but I can't get my head around one thing, and I wonder if there are people around here with experience on this particular aspect.
In my app I let the user select an image from his library, ultimately resulting in an upload. (An interesting thing here, is that the images in there may originate from 12-megapixel high quality camera's, since e.g. iTunes happily syncs them into the phone.)
For various reasons, I UIImageJPEGRepresentation the thing right away to a locally stored file.
Without much thought, I hung on to the UIImage returned by the picker.
In Instruments, on the simulator, I see that the UIImage returned by the UIImagePickerController releases the memory. Does this indeed mean it's being backed by the file in the library, which we cannot access?
Does this also mean, that the advice in the answer linked to above, to store the image right away when needing the full resolution, only leads to additional overhead? (at least for the pictures from the library)
Furthermore, an image taken with the camera, may or may not have some internal backing, which I haven't investigated yet (Why can't the simulator just use my iSight?!). If not, it would surely be a memory hog and the first thing to do would be an UIImageJPEGRepresentation followed by an UIImage imageWithContentsOfFile: to enable the backing, which would come at the cost of quite a delay.
Anyone any thoughts?