What is the best way of loading images from the application main bundle. I am using
[UIImage imageNamed: "image.png"];
But I have heard that this is not good in terms of memory usage and performance. Can anyone please give his/her feedback on it? My application reads lots of images from main bundle at launch time. I want to make this process as efficient as possible.
Best Regards
If there were one true "Best Way", the means to load images multiple ways would not exist (unless for historical reasons). Therefore, a little understanding will serve you better than distilling an answer down to a "Best Way".
+[UIImage imageNamed:] caches the image for reuse, and it is a sensible default for most purposes. Caching is excellent if used correctly. The cache is good because it can minimize your disk reads and memory usage by sharing and reusing loaded images, rather than reading and allocating a copy for each image you must display. Consider an icon image which you use on multiple screens - would you like that image data to be read and reallocated each time? This may result in redundant reads and allocations of identical image data. If not, use the caching methods.
If you load the image only once and lazily, then you may want consider non-caching approaches.
Image data can consume a lot of memory.
Reading an image can take a long time -- not just disk i/o, but also converting it into a usable UIImage representation (e.g. decompressing the image).
there are also times where you should resize/scale an image. then you'd want a scaled copy.
In short, there are many considerations, but if you have properly scaled assets which you reuse, caching is typically the right choice.
If your assets are not sized properly, then the issue is more fundamental -- you should resize the bundled assets to be appropriate for the purpose if you're experiencing performance problems. Properly sized images also make drawing significantly simpler while retaining the best image quality.
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"myimage" ofType:#"png"];
UIImage *image = [UIImage imageWithContentsOfFile:filePath];
If you want to try loading the images without caching, you can use:
[[UIImage alloc] initWithContentsOfFile:#"image.png"];
This could be slightly faster loading the image for the first time, since it doesn't have to cache it first. If you only need to use these images once, it probably makes sense to try it this way. The other benefit of doing it this way is that you won't have an image cache using up memory for longer than necessary.
For finding a single resource file using NSBundle
NSBundle* myBundle = [NSBundle mainBundle];
NSString* myImage = [myBundle pathForResource:#"Seagull" ofType:#"jpg"];
and for finding multiple resources:
NSBundle* myBundle = [NSBundle mainBundle];
NSArray* myImages = [myBundle pathsForResourcesOfType:#"jpg"
inDirectory:nil];
Related
I have some performance problem with NSData dataWithContentsOfURL...
NSURL *url = [NSURL URLWithString:Imagepath];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img=[UIImage imageWithData:data];
[ArrayImages addObject:img];
This code is placed in a method that manage a JSON response got from an NSUrl connection(after calling my web service). All the code in this method is already in a background thread, moving this piece of code out the background thread do not solve the issue.. All the retrieved image are placed in a view in main thread. What i can do to make dataWithContentsOfURL faster or there is a alternative to dataWithContentsOfURL?
Thanks In Advance
+[NSData dataWithContentsOfURL:] is not "slow". If loading one image takes a long time, the problem lies elsewhere.
Evaluate your problem. For starters:
Which resource is the bottleneck? Probably the Network.
How do you load images? All at once? That would be bad -- display them as they become ready.
What are the sizes of the images? I saw one SO question where the poster wanted to load 50 MB images. That's way too large. As well, if all you need is a thumbnail, then be sure you request the thumbnail from the server and load that and not the full-sized image.
Are you loading things you don't even need to display? Wait until you need to display them.
How many threads are you using for Network tasks? For CPU? For I/O?
Are your source images properly "crushed"?
Write your program so it flows with your program's presentation model. Example: I had a bazillion images to display in tables, but I made sure to minimize resource usage and made sure load and request cancellation was well supported for the app. This was all coming through the network, and it was plenty fast (it was network-bound).
and if you are loading many images from device storage, you should consider using -[UIImage initWithContentsOfFile:] instead because your image data would not be cached, but can be purged.
U need to use Lazy loading for images display as content will displayed as image gets downloaded.
Use SDWebImage which uses lazy loading of images.
I just figured out that - instead of using a UIImageView to show an image - I use an UIWebView, it uses much less memory. Does that make sense for anyone?
What I did was to get an ordinary .jpg file with about 280kb in size and show it on a simple app with just an UIImageView. Looking at Instrument's Object Allocations tool, the application's memory footprint was about 3MB.
Then I removed the UIImageView and added an UIWebView with the following code:
NSString *html = #"<html><body style='margin:0;'><img src='my_test_image.jpg'></body></html>";
[web loadHTMLString:html baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]];
and then the memory usage dropped to around 700kb.
Any thoughts?
The UIWebView scale your image and crop it (When it is higher than the screen size...) It is an optimization from Apple in order to easily print every image on a website with less memory usage... The image quality change with the image size.
The UIImageView doesn't have that kind of optimization.
I don't think it will have such a major difference, even the oldest iPod Touch has 128MB of RAM. Just alloc and release it all correctly and you should be fine either way.
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.
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.
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.