iPhone/iPad UIImage initWithContentsOfFile: - iphone

I have written code that when you hit button, it opens new screen with image on it. On that new screen there is button that dismisses screen, and returns to main screen. And it works fine if i do it like this (no leaks etc...):
img = [UIImage imageNamed: #"Galaxy"];
ImageDisplay *display = [[ImageDisplay alloc] initWithImage:img];
But if i replace this line of code with something like this:
img = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Minnesota" ofType:#"png"]];
ImageDisplay *display = [[ImageDisplay alloc] initWithImage:img];
[img release];
It acts as i have memory leak. Every time i open image screen, app takes more and more memory. But all deallocs are called, even [img retainCount] shows 1 before final release. Is there possibility that there is a bug here, because i cant find whats wrong?
EDIT:
Here is dealloc method for ImageDisplay, and this method gets called:
-(void) dealloc {
[img release];
[super dealloc];
}

Your ImageDisplay *display is retaining the image. As it should be. When you release that, it should release all its retained entities. In the code you've shown, you're not releasing it. The typical use would be to tell the containing view controller to display it modally or something (or push it onto a navigation controller) and release it, leaving its retain lifecycle in the hands of whatever view controller is now managing it. The difference is, in your first code sample, *img is autoreleased, and will release itself when appropriate, and in the second, it's not.
ARC would save your bacon here, and dramatically simplify your code.
Also you should google the term "static method", because you're working really hard to call static methods as instances of the class of objects, which is like going around your ass to get to your elbow.
ALSO, stop looking at retainCount. All sorts of things might retain your objects under the hood of the framework. Using retainCount as part of your debugging strategy is a one way ticket to confusionville.

Try using this one instead, you dont need to allocate and release:
[UIImage imageWithContentsOfFile:(NSString *)name]

Note that since imageNamed: is a class method not an instance method, you use it like this:
UIImage *myImage = [UIImage imageNamed:#"pony.png"];
Your posted code using initWithContentsOfFile looks correct, so the leak must be somewhere in your ImageDisplay class.

Related

Releasing images not created with init

Hi every one I have the next issue I'm creating various images like this
UIImage *image = [UIImage imageNamed:_imageName];
and I'm assigning to one UIImageView
[self.imgvImage setImage:image];
The user when tap a button one UIImageView is created and one UIImage is assigning, I have 4 UIImageViews created when new is created the last is removed sending release to the UIImageView like this
[_imgvImage release];
But around than 100 times creating and releasing one memory warring happen but the app dont crash I think this is because the UIImages created are not releasing and there are so much because all the code are clean and creating and releasing are all fine.
How can I remove all the UIIMageView with his UIImage completely from the memory.
Please help, sorry for my English in not good.
The image created using below statement is handled by iOS. this image is stored in a cache so that next time you call this it is simply get returned from the cache.
UIImage *image = [UIImage imageNamed:_imageName];
I think you should not worry about memory warning as long as you are releasing what you are allocating (UIImageView in this case). iOS should clear this cache in case of memory crunch.
If you do not want that caching (e.g. when you are loading one image once only) you can use imageWithContentsOfFile: method of UIImage.
Hi I found the solution the solution was call the
NSAutoreleasePool
in the method when I removed the objects of the view like this:
for(ProductVW *pVW in [_vwProductsContainer subviews]){
NSAutoreleasePool * autoreleasePool = [[NSAutoreleasePool alloc] init];
[pVW removeFromSuperview];
[autoreleasePool release];
}

Using UIImageView without being a memory hog?

UIImageView *mooshinLogo = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"mooshin.png"]];
I'm not sure if im instantiating my image correctly because i've been reading that imageNamed will not be emptied from the cache and should only be used for buttons and small icons. What about background images, and image galleries?
How do I implement UIImageView the right way?
imageNamed: method caches images that you load. In case of low memory conditions or memory warning that cache will be emptied.
If you want to show images in gallery it is better to use imageWithContentsOfFile: method as it doesn't cache the data.
In case of very big images you should use CATiledLayer to display your image using tiles.
No worries! The method imageNamed returns an autoreleased object, and it will do what you call "be emptied from cache" (i.e. its memory will be released) when it is not needed any more.
[UIImage imageNamed:(NSString *)imageName] is an autorelease convenience constructor. This means that it's retain count is increased when you initialize it and decreased at the end of the runloop. You could make 100 of them and they'd vanish from memory a few seconds later, unless they're retained by something else. By passing it to the UIImageView, the UIImageView will retain it and it will stay in memory only until the UIImageView is done with it, so you're doing that correctly, unless you're referring to the OS caching the image in "inactive" RAM. It may do that behind the scenes (and will know when to get rid of it), but you're certainly handling the object's lifecycle correctly. Since you're using the [[Class alloc] init...] way to construct your UIImageView, make sure you later call [mooshinLogo release] or [mooshinLogo autorelease].
(Please just ignore this if you already know.) Objective-C (at least, for iOS development) is a reference counted language. All objects start out with a reference count, or retain count, of 1, from the time they are alloc'd. From there, they can be retained ( [id retain] ), released ( [id release] ), or marked to be released at the end of the runloop ([id autorelease] ). Once the count is zero, they will be dealloc'd, but you should never concern yourself with it's actual retain count and only use objects you own (or are retaining).
When in doubt, you can check with Clang's Static Analyzer. It's finds probably 75% of the your leaks, and I've only had a handful of false positives. Either Build & Analyze, or Cmd+Shift+B.
Others above are right about imageNamed keeps a cache of these images. This is especially true when using NIB files. Releasing a Viewcontroller with ImageViews on them doesn't directly release the associated images.
I had an app that had lots of images with lots of pages in a navigation controller. Eventually it would crash when only using imageNamed method. So, I now use (found here on SO) and over:
+ (UIImage *)imageNamed:(NSString *)name {
//NSLog(#"ImageNamed: %#", name);
return [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:name ofType:nil]];
}
Now I can control when I want to remove an image and and make it actually clear from memory. The aforementioned app, I would actually load the images on viewDidLoad and remove them on viewDidDisappear. So, when I was 10-15 pages deep in the NavController, I could keep memory down.

memory leak issue

I am loading images to UITableViewCell using the following function. But when I run my app with Debugger its getting crashed at [pool release] whenever I scroll my UITableView. What might I do to solve this? Thanks in advance.
- (void) loadImage{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
imageURL = [imageURL stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
//NSLog(#"img url cell**** %#",imageURL);
self.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageURL]]];
//self.image = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageURL]];
[pool release];
}
By the fact that you are using a NSAutoreleasePool I guess that load image is running in a thread that is not the main thread. Is this Correct? If that is the case you are making a UIKit invocation (self.image = ...) in this non-main thread and this is a possible source of the crash you are experiencing. All UIKit updates must be made in the main thread, since UIKit is not thread safe. Try replacing:
self.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageURL]]];
by
[self performSelectorOnMainThread:#selector(setImage:) withObject:[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageURL]]] waitUntilDone:YES];
Notice I'm guessing the name of the setter is setImage:, you may need to correct if the setter selector has a different name that.
First of all, it is recommended to use [pool drain] instead of [pool release].
But I see another potential issue here: according to your code, you defined initially imageURL outside of your newly created autorelease pool (I don't see any alloc/init for imageURL, where is coming from? is it a table cell instance?) then you reassign imageURL with a newly allocated and autoreleased string: [imageURL stringByAddingPercentEscapesUsingEncoding:...]. Finally when you release the pool the new imageURL is release, so at the end what you had is a leak of the previously allocated imageURL. I can expect at this point that when exiting from the inner autorelease pool the run loop autorelease tries to release imageURL again (for example, but I need to see your code to fully understand) which has been deallocated.
Can you try to assign the result of stringByAddingPercentEscapesUsingEncoding: to a different name (e.g. myImageURL)?
Quite a few things wrong here.
Firstly, your need of an autorelease pool quite clearly suggests that you're not doing this work on the main thread. UIKit is not thread-safe. Any UI work needs to be done on the main thread or undefined behaviour will occur, such as crashes.
Secondly, you're using what looks like a synchronous URL download of an image. Correct me if you're loading it from a file URL on disk. A synchronous image download is probably the reason why you've moved this into a separate thread, because it was blocking your UI right? One of the first things you should learn about network access it to never use a synchronous API on the main thread. Have you considered looking at the NSURLConnection class in order to do an asynchronous download?
Thirdly, this needs to be re-architected so that the image download isn't directly linked to the display of the cell.
Displaying the table cell should be done on the main thread with the rest of the UI. When the cell is displayed, it should make a call to download the image asynchronously. The image download should be cached, and the cell should be informed via a delegate callback when the image download is complete. It should then display the image.
You can wrap these kind of things up in NSOperations and use NSOperationQueue to manage them. Please, look at the documentation for these classes and checkout the example code for them to learn how to use them.

My iPhone app crashes after returning from background. Cause: UIImage problem

First off, I want to say this site is AWESOME! and it helped me do lots of stuff while creating my iPhone app.
Now, my problem is:
When I launch my app, I have a UIImageView that loads an image depending on an if/else statements in
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
method. These images are assigned as follows:
BG.image = someImage;
of course, BG is the UIImageView, and someImage is an iVar with #property, #synthesis. someImage is initialized with an image from the main bundle in viewDidLoad:
- (void)viewDidLoad {
//init stuff from file
someImage = [UIImage imageNamed:#"FirstViewBG_5N.png"];
[super viewDidLoad];}
My app runs happily, loading images according to touchBegan (as mentioned), BUT!
When my app is sent to background and comes back, it crashes upon first touch.
When I replaced:
BG.image = someImage
with:
BG.image = [UIImage imageNamed:#"FirstViewBG_5N.png"];
it runs happily?! I think the someImage is flushed or corrupts?
I don't want to leave it like this because imageNamed method reads from disk every time, which will cause performance problems, i think?
I think my question is clear? It is that:
1- Why will my app crash after returning from backgroud
2- How do I solve this?
All your help is appreciated!
Thanks!
I'm guessing the crash is EXC_BAD_ACCESS (but am guessing because you didn't post that information).
If "someImage" is an instance variable, you should synthesize it and use its accessor (self.someImage) so it is retained or copied. As it stands, you're assigning something to someImage but it's gone by the time you're trying to access it later.
When you do this:
- (void)viewDidLoad {
//init stuff from file
someImage = [UIImage imageNamed:#"FirstViewBG_5N.png"];
[super viewDidLoad];
}
The imageNamed method is returning an autoreleased object which gets cleaned up by the garbage collector after viewDidLoad returns. Try either retaining it:
someImage = [[UIImage imageNamed:#"FirstViewBG_5N.png"] retain];
or using your synthesized setter which will retain it automatically:
[self setSomeImage:[UIImage imageNamed:#"FirstViewBG_5N.png"]];
or using UIImage's initWithData initializer:
someImage = [[UIImage alloc] initWithContentsOfFile:#"FirstViewBG_5N.png"];
All are functionally equivalent. #2 or #3 is best.
Apple's "Memory Management Rules" guide will save your life: http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html%23//apple_ref/doc/uid/20000994-BAJHFBGH

Quartz caching CGLayer

To quote from the CGLayer doc:
Quartz caches any objects that are reused, including CGLayer objects.
I am having problems with memory on large pages and am trying to implement a simple mechanism where views are released and recreated based on whether they are on/off screen.
Say for the sake of simplicity that I have a bunch of UIImages, created as the result of a network request, saved in an array somewhere. I create a UIImageView like so:
anImage = [anArray objectAtIndex:0];
UIImageView* imgView = [[UIImageView alloc] initWithImage:anImage];
[mainView addSubview:imgView]; // Quartz eats memory for view after first draw
[imgView release]; // owned by mainView now
[...] // wait a bit for draw cycle
[imgView removeFromSuperview]; // memory doesn't go down
When the imgView goes offscreen it is removedFromSuperview and released. Fine right? Nope- the CGLayer that exists in Quartz is not removed, because anImage still exists.
How can I get around this? The only way in this scenario is to create an image exactly the same behind Quartz's back with a different pointer address and delete the old image. And the only way to do this is to 'deep copy' the image (UIImage doesn't implement NSCoding) or to ask for it again over the network (slow).
What I am thinking is that I need to sqllite my images to a database and refetch them every time a view comes onscreen- but I would love to hear people's thoughts on this.
Here you increment imgView from 0 to 1.
UIImageView* imgView = [[UIImageView alloc] initWithImage:anImage];
In the next line, the mainView incrementes the reference count. (now it's 2)
[mainView addSubview:imgView]; // Quartz eats memory for view after first draw
Here, you release the imgView and the reference count goes back down to one.
[imgView release]; // owned by mainView now
I don't think your memory issues have anything to do with anImage. As long as imgView is a subview, it won't free that object, because it needs that object to draw to the screen.
What the following line means, is that if you programmatically draw to your CGLayer, Quartz with cache what you've drawn, so that you aren't constantly redrawing the same thing. It's not really related to adding subViews.
Quartz caches any objects that are reused, including CGLayer objects.