Cutting down UIImage memory usage - iphone

I'm trying to find ways to cut down memory usage for my app right now, and I have an idea regarding UIImages. I make pretty extensive usage of UIImages, some of them common to multiple views. Is it a lot more memory efficient to instantiate a single UIImage and then use a pointer to that same image throughout the app rather than allocating a new UIImage for the same image in each view? Or, is the OS smart enough to sort of automatically cache a UIImage so that it is only stored in memory once? (doubt that's the case but I have to ask)
Thanks.

Straight from UIImage apple reference docs for imageNamed:
This method looks in the system caches for an image object with the
specified name and returns that object if it exists. If a matching
image object is not already in the cache, this method loads the image
data from the specified file, caches it, and then returns the
resulting object.

You use a lot of images, so remember that [UIImage imageNamed:#""]; caches the images, so if you are having low memory problem use instead [UIImage imageWithContentsOfFile:#""]; that doesn't cache it. Maybe that can help you cut down memory usage.

When a view unloads set the GUI properties to nil in the viewDidUnload method of UIViewController subclasses.

Yes, it is more efficient to store a single UIImage and use pointers to it.
It is unlikely that the OS caches images in this way, and even if it did, using pointers gives you more fine control of when the image is released / deallocated so that you don't have to hope the OS is going to take care of it.

Related

Images not freed on memory warning when using [UIImage imageNamed]

In my app, I've used the line
[UIImage imageNamed:imageName]
whenever loading an image. As I understand it, this function caches images for later use - this is great, because most of the images are used more than once.
BUT - when I simulate a memory warning, the memory used by these images doesn't seem to be freed according - and since most of the memory is in fact used by the cached images, it is pretty important that any currently unneeded images are released from memory!
-
Is this the expected behavior, or do the images remain in memory due to the fact that it's only a simulated memory warning? Or, if I want to release these images, will I have to write my own category for UIImage that caches images but releases uneeded ones when memory is in short supply (or even better, has anyone already done this/shared the code for it)??
What I have done is create my own caching mechanism utilizing -initWithConentsOfFile which doesnt cache, and an NSMutableDictionary for storing, every image allocated is stored in the dictionary with the image name as the key. If a memory warning is reached you can release and nil the dictionary thus releasing all the memory allocated from the images.
This gives you complete control over the image caching. -imageNamed: uses its own caching mechanism that you have no control over when and where the allocated memory is released.
You should try this method as well.
EDIT: Heres my UIImage class extension (https://gist.github.com/2880240), overriding -imageNamed: with custom cache, upon receiving a memory warning you can simply use [UIImage freeCache]. Also included, is the ability to set autorelease on all images upon storing them in the cache, i.e: [UIImage setShouldAutorelease:TRUE]. FALSE by default.
Give it a try, Hope it helps!
If there is no more reference to the image it should get cleaned, don't worry about the internal caching mechanism (since its behavior is undocumented anyway). Do you have any code in place to remove any references (nil or release) to the images?
Are you using it in a UIImageView?
A lot of framework data is automatically managed with the app goes into the background. Data for images loaded with imageNamed: are discarded automatically, but UIImageView does not discard its data, so perhaps its the same with the low memory warning.
P.S.
If your app is suspended it won't receive the memory warning. And if its using a lot of memory then it'll probably be terminated. To reduce the chances of this (if its important) then you could use an NSPurgeableData which you store in an NSCache in order to flag stuff as being purgeable in the event of a low memory situation, the OS will then purge it for you.

Using system cache of UIImage objects with images loaded from disk cache (NSCachesDirectory)?

I'm trying to improve scrolling performance on a UITableView that uses cells with images fetched from the web, but stored in the NSCachesDirectory. The cells have a custom content view to draw the contents (an image).
When I use a placeholder image from the app bundle, using [UIImage imageNamed:#"Placeholder.png"], scrolling performance is super fast.
When I load an image from the disk cache (NSCachesDirectory) using [UIImage imageWithContentsOfFile:cachePath], scrolling performance gets worse.
According to the documentation, imageNamed: caches the image and imageWithContentsOfFile: does not.
How to use UIImage's system cache when using imageWithContentsOfFile: ?
Thanks a bunch!
It seems to be possible to use the path to an image in the NSCachesDirectory as argument for the [UIImage imageNamed:] method. The method accepts relative paths (relative to the app bundle), e.g.: #"../Library/Caches/SomeCachedImage.png" works.
UIImage automatically caches the image in memory if it is used multiple times, which improves the performance when an image is used multiple times in a table view.
The problem is most likely that you are loading and decompressing the image in the main run loop. This will block the user interface for a short time. You get much better performance if you do the loading and decompression in a seperate thread and only set the image in the main loop. (Which is also required for user interface changes, which setting an image on a UIImageView is)
This will require some more infrastructure. Like for example a notification scheme or key value observing.
You can't. imageWithContentsOfFile: will always load the image from file (though lazily). What you can do is create an in memory cache of you own with NSArrays or NSDictionaries, depending on how you'll want to do the lookup.

Best performance when using a stretchable UIImage

I need to use a stretchable UIImage hundreds of times in my app in multiple UIImageViews. Is it okay to globally reuse the same stretchable UIImage instead of having to recreate them in memory each time I need to add it to a UIImageView?
I know [UIImage imageNamed:] caches images for better performance, but this cannot be used for stretchable images!
Thanks,
Mike
Oh, absolutely, you should always store your image once and then call that same image every time you want to draw it. Typically for projects with a lot of images I have a single SpriteManager class that contains an NSDictionary full of sprites. Then I can reference each one via filename. This way each image is loaded only once, which is significantly faster.
The absolutely most efficient way to do this, though, is just to use OpenGL ES. And if you've got hundreds of images being drawn, I absolutely recommend that you do so.
Definitely: Reuse your image. Are you storing it in a global variable? Make sure you retain the image (or put it in a retained property fo some global class like your Application) since it probably came AutoReleased.

Would such assignment of image cause a memory leak?

- (void)viewDidLoad {
[super viewDidLoad];
landscape.image = [UIImage imageNamed:#"tenerife1.png"];
}
I assign a new UIImage to the image property of an UIImageView object. I am not sure if that would result in a memory leak?
No, it should not. The old image should be automatically released when you set the new one, and the "imageNamed" method uses autorelease, so you should be OK there.
hey take into account imageNamed has serious memory issues as you loose control over its cache - ie: once you are done with your image, you cannot reclaim that memory. a quick google search would let you know how many people have faced problems with imageNamed
i was at the apple iphone tech talks and the guy giving the presentation confirmed the same damn thing - he suggested using imageWithContentsOfFile instead of imageNamed
if you just have couple of small images, its fine otherwise use imageWithContentsOfFile even though its a bit slower - and implement your own caching logic - check this great link on how to do it here
It depends on how the image property is defined. If it's defined as retain or, I suppose, even copy, it should be fine. You'll end up trying to reference deallocated memory and crashing your program if it's defined as assign.
Not ordinarily, but it would depend on how you've defined landscape.image. See post above. Be careful with using a lot of these:
[UIImage imageNamed:#"tenerife1.png"];
Since there is a tendency for these images to fill up memory, without getting released.

Difference between [UIImage imageNamed...] and [UIImage imageWithData...]?

I want to load some images into my application from the file system. There's 2 easy ways to do this:
[UIImage imageNamed:fullFileName]
or:
NSString *fileLocation = [[NSBundle mainBundle] pathForResource:fileName ofType:extension];
NSData *imageData = [NSData dataWithContentsOfFile:fileLocation];
[UIImage imageWithData:imageData];
I prefer the first one because it's a lot less code, but I have seen some people saying that the image is cached and that this method uses more memory? Since I don't trust people on most other forums, I thought I'd ask the question here, is there any practical difference, and if so which one is 'better'?
I have tried profiling my app using the Object Allocation instrument, and I can't see any practical difference, though I have only tried in the simulator, and not on an iPhone itself.
It depends on what you're doing with the image. The imageNamed: method does cache the image, but in many cases that's going to help with memory use. For example, if you load an image 10 times to display along with some text in a table view, UIImage will only keep a single representation of that image in memory instead of allocating 10 separate objects. On the other hand, if you have a very large image and you're not re-using it, you might want to load the image from a data object to make sure it's removed from memory when you're done.
If you don't have any huge images, I wouldn't worry about it. Unless you see a problem (and kudos for checking Object Allocation instead of preemptively optimizing), I would choose less lines of code over negligible memory improvements.
In my experience [UIImage imageNamed:] has dramatically better performance, especially when used in UITableViews.
It's not just the memory but also decoding the image. Having it cached is much faster.
As the API reference of UIImage says :
+(UIImage *)imageNamed:(NSString *)name
This method looks in the system caches for an image object with the specified name and returns that object if it exists. If a matching image object is not already in the cache, this method loads the image data from the specified file, caches it, and then returns the resulting object.
+(UIImage *)imageWithContentsOfFile:(NSString *)path
This method does not cache the image object.
so,we can see that if you have a lot of same UI elements(such as UITableViewCell) that may use same image(often as an icons),and due to performance , of course we want to reuse the same image , so that we will save some memory for other use . Generrally the reused image is often used in the ui element that our user may operate on it lots of times . So it values for us to reuse it .So you can choose to use imageNamed method .
And on the other hand , in an application , there will be some UI element that will be there during the app's life cycle,such as a Button , a logo view , so these images used by these ui elements may also be there during the app's life cycle ,you wouldn't consider whether these image should be cache or not .So you can choose to use imageNamed method .
On the contrary,in an application , there are often some UI Elements that created dynamically. For example , our application support dynamic background , so that user can choose the background they like .And the background may be an image .So we may have a interface that list lots of different background (often show by use UIImageView) for user to choose ,we can name the list view MyBackgroundListView.So once the user chooses an background image , the MyBackgroundListView should be destroyed , because it has finishs its function .The next time the user want to change his/her background , we can create MyBackgroundListView again .So the images used by MyBackgroundListView shouldn't be cached , or our application's memory will run out .So this time you should use
imageWithContentsOfFile method.
As the Apple's doc Supporting High-Resolution Screens In Views says
On devices with high-resolution screens, the imageNamed:, imageWithContentsOfFile:, and initWithContentsOfFile: methods automatically looks for a version of the requested image with the #2x modifier in its name. If it finds one, it loads that image instead. If you do not provide a high-resolution version of a given image, the image object still loads a standard-resolution image (if one exists) and scales it during drawing.
so you would worry about the image's search path for retina screen problem . IOS will help you deal with it.
Sorry for my poor English . May it be helpful.
If you don't want your image do be cached you can also use initWithContentsOfFile directly :
NSString *fileLocation = [[NSBundle mainBundle] pathForResource:fileName ofType:extension];
UIImage* yourImage = [[[UIImage alloc] initWithContentsOfFile:imagePath] autorelease];
I've also been told that [UIImage imageNamed:] does a little bit too much caching, and images are not often released. I was told to be careful of using it.
imageWithData is useful when you store your image binary in a database or progressively downloading large image from the web.
I would not use imagenamed if your app has loads of big images which are not the same. I experienced app crashing due to using too much of it.
I don't believe that the image gets cached at all, and I don't know why you are all saying that. UIImage is a subclass of NSObject which uses reference counters to keep track of the things that it is related to. So when you load an image it does that same thing. If you load the same image multiple times it will(or should) have only one copy of the image in memory and just increment the reference counter every time you have to use something with that image. By Reference Counters I mean that when the count gets to 0 it deletes itself. so "alloc", "retain" are each +1 to the count and "release" is -1. Not only is it a better way to manage memory but this style of programming also helps clean up memory leaks.