mem allocation with imageNamed - iphone

I want to display some images, when image is not available I want to show a default one.
When using the analyze functionality I get warnings about a potential leak.
I do under stand that when using imageNamed there is no memory allocated, what would be a nice workaround ?
See below a part of my code
if (!isMyFileThere){
image = [UIImage imageNamed:#"default.png"];
}
else{
image = [[UIImage alloc] initWithContentsOfFile:pngFilePath];
}

This is autoreleased
image = [UIImage imageNamed:#"default.png"];
This is not
image = [[UIImage alloc] initWithContentsOfFile:pngFilePath];
You need to do this :
image = [[[UIImage alloc] initWithContentsOfFile:pngFilePath] autorelease];
The rule is if your method name begins with alloc, new, copy or muteableCopy you own it and need to release it yourself, either with release or with autorelease. Anything else isn't yours so you mustn't release it.
If you call retain on an object, you must release (or autorelease) it the same number of times :)

image = [[UIImage alloc] initWithContentsOfFile:pngFilePath]; You have done a alloc and you have to now release it, which is a potential leak if you don't. The other statement is autoreleased object.

If you want to object to stay until you release it manually you should use retain, autorelease adds the object to the current NSAutorelease pool which gets drained at the end of each run loop iteration. if you attempt to use a freed object your program will crash.
in iOS 5.0 if you enable ARC you won't need to use "retain", "autorelease" or "release" anymore. those are added by the compiler automatically.

Related

iPhone: Confusion about NSAutoreleasePool

I read about NSAutoReleasePool and understand that it makes memory management easier on iPhone. It is available on NSObject so autorelease message available for every object instance. Moreover, I shouldn't use autorelease a lot as NSAutoReleasePool uses cache memory and you might runout of memory if there are plenty autoreleased objects. Is my understanding correct?
One thing I didn't understand is what is the purpose of creating NSAutoreleasePool explicitly like it's done in following method? What its purpose here? Is it like releasing imgData, img objects automatically? Because I could see that these objects are not released in this method.
- (void)loadImage {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImage *img = [UIImage imageNamed: #"default_user.png"];
if(![[imgURL absoluteString] isEqualToString: #"0"]) {
NSData *imgData = [NSData dataWithContentsOfURL: imgURL];
img = [UIImage imageWithData: imgData];
}
if([target respondsToSelector: action])
[target performSelectorOnMainThread: action withObject: img waitUntilDone: YES];
[pool release];
}
Thanks.
You can use local autorelease pools to help reduce peak memory
footprint. When your pool is drained, the temporary objects are
released, which typically results in their deallocation thereby
reducing the program’s memory footprint.
I can suggest this explicit autorelease pool was created for manage image loading. Probably that image has a large size (in Mb) and this pool can guarantee that memory will released asap. All autorelease pools organized in stack, so this inner pool will drain early than main pool.
The idea behind auto-release to keep the memory usage of the app low. You see if you haven't used this auto release then this data would have gone to the main autorelease of the app So even if you don't need this image further it still stays in the memory and increases its footprint. Creating a new auto release frees the memory straight away.(the size of the image can be huge.)
The rule is that you must create an autorelease pool in every thread that will use autorelease. In your example, img variable is autoreleased, and assuming the loadImage method is threaded, you must declare a new pool before using any autoreleased memory.

Whats the correct way to alloc a UIImage to memory and release it iphone

Using Instruments, I keep on getting pointed to a memory leak with a UIImage.
I think I'm assigning and releasing the memory correctly. The leaked object in instruments is described as NSConcreteData
Is the following the correct way to assign and release a UIImage?
UIImage* flagimg = [UIImage imageWithData: [NSData dataWithContentsOfURL:url2]];
[flagimg release];
flagimg =nil;
[UIImage imageWithData:] returns an autoreleased object, which should not be released by you again. So this code snipped contains not a memory leak but the opposite, a double free (in the worst case).
Note that Instruments sometimes generates false positives and/or reports memory leaks in the Foundation itself (yep, they make mistakes too :-).
The fastest way to alloc/release an object is to avoid convenience initializers (like imageWithData:) and instead to something like
NSData* data = [[NSData alloc] initWithContentsOfURL:url]];
UIImage* img = [[UIImage alloc] initWithData:data];
[data release];
// use your image
[img release];
This will allocate and release your object right away and not wait until the autorelease pool is cleaned.
But please note too, that a memory leak is generally not memory that is not yet freed, but that is lost and cannot be freed anymore, so an object which will be deallocated by the autorelease pool is not considered a memory leak.
as a general rule you can say
if you create an object an theres a "init","copy" or "retain" in it, you have to release it.
if not, you get an autoreleased object.
thats not always true, but in most cases
both imageWithData and dataWithContentsOfURL return autoreleased objects, so you should have no memory leaks in that code snippet.
Since flagimg is returned autoreleased, your [flagimg release]; call is not needed; you're over-releasing that object.

Caching behavior of UIImage

Does UIImage ever removes images from its cache? Can I keep a pointer to an image I got from imageNamed: and use it as long as I like or must I always call imageNamed:?
The UIImage object that is returned from imageNamed: is treated like all other objects as far a memory management goes. If you want to keep the reference to the object between method calls, you should retain it and release it when you are done to decrement the reference count.
UIImage * cachedImage;
-(void) getTheImage {
UIImage * cachedImage = [[UImage imageNamed:#"MyImage.png"] retain];
//Do something with the image...
}
//In some other method or dealloc
[cachedImage release];
Also, note that the UIImage class reference says:
In low-memory situations, image data
may be purged from a UIImage object to
free up memory on the system. This
purging behavior affects only the
image data stored internally by the
UIImage object and not the object
itself. When you attempt to draw an
image whose data has been purged, the
image object automatically reloads the
data from its original file. This
extra load step, however, may incur a
small performance penalty.
UIImage caches the data itself. You must not hold a pointer and just pass that around. That can be unsafe since when there is a memory warning and there was no strong ref to that object then UIImage will purge cached data. Call [UIImage imageNamed:] every time. It is fast and returns the ref to the image from memory. If the image is no longer in memory it will reload it and pass that ref

objective-c memory management: caching view elements

I'm writing an application which is quite graphically heavy and therefore I'm trying to implement a caching mechanism within my view controller that creates a view once, and retains it for future use, similar to the following:
- (UIView *)logoView
{
if(_logoView == nil)
{
_logoView = [[UIImageView alloc] initWithImage: [UIImage imageNamed: #"logo_header.png"]];
_logoView.contentMode = UIViewContentModeCenter;
}
return _logoView;
}
This all works fantastically, however, I'm getting a bit confused over a certain issue; when I simulate a memory warning I call a method which purges the cache, like so:
- (void)purgeCachedContent
{
if(_logoView != nil)[_logoView release];
}
However, this seems to be causing my application issues, as it can get called several times (dealloc, didReceiveMemoryWarning and viewDidUnload).
How can I determine if an instance exists to send it a release message? Even when I try to NSLog a released view I receive a EXC_BAD_ACCESS error, so I'm having trouble figuring out the best way I'd do it. I even removed the if statement hoping to rely on being able to send nil objects messages, but this causes the same error...
I'm sure it's something simple missing from my objective-c knowledge... I just don't know what!
_logoView isn't set to nil automatically just by releasing it, so any future methods you try to call using that pointer will go to a memory location that used to contain a valid object, but now contains junk. You can fix this by setting _logoView = nil; after releasing it.
If you want to cache it why would you want to release it?
just use autorelease on init:
_logoView = [[[UIImageView alloc] initWithImage: [UIImage imageNamed: #"logo_header.png"]] autorelease];

Memory issue in iPhone?

I am using single UIImageView object, allocated memory it once. But changing the image names multiple times.
The question is that,
A) one UIImageView object,allocated once and never changed image name.
e.g UIImageView *firstObj = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"blue.png"]];
B) another UIImageView object,allocated once and changed image names multiple times
e.g UIImageView *secondObj = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"blue.png"]];
//changed image name once,
secondObj.image = [UIImage imageNamed:#"green.png"];
and so on....
which objects uses maximum memory or use equal memory?
which is the best way to use secondObj with minimum memory utilization?
please explain it briefly because i need to use number of image in my project and i want to avoid memory issue due to the images.
Reformatting your code, your second example is:
UIImageView *secondObj = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"blue.png"]];
// changed image name once
secondObj.image = [UIImage imageNamed:#"green.png"];
// and so on....
This code is fine. When you assign an image to an instance of UIImageView, it will increase the image's retain count by one. If an image is already assiged, it will release it first.
Since [UIImage imageNamed:...] will give you an object already marked for autorelease, you can continue to assign images as shown in the example with no memory leaks. Once the UIImageView releases the existing image, it will be collected by an Autorelease pool.
In terms of minimising memory usage, the [UIImage imageNamed:...] method does store the image in a small amount of application-wide cache memory that you don't exert any direct control over. The cache does have an upper limit limit, but you can't flush it to reclaim memory, so using it will increase your memory footprint as you fetch new UIImages.
You may want to consider avoiding this cache by using [UIImage imageWithData:...] to load your images, which is discussed in the Stackoverflow question [UIImage imageNamed…] vs [UIImage imageWithData…].
I'm no great Objective-C expert but your usage in B looks like it should work without leaking. Since no other objects (other than the UIImageView) are retaining the UIImages, they should be released when they are replaced in the UIImageView and the garbage collector should do its work.
David
A UIImage created using +imageNamed: is autoreleased so it will go away. To give an example:
UIImage *image1 = [UIImage imageNamed:#"blue.png"]; //image1 has retain count 1
[myImageView setImage:image1]; //image1 is retained by UIImageView so has retain count 2
//run loop ends, all auto released objects are released and image1 now has retain count 1
//Later in app
UIImage *image2 = [UIImage imageNamed:#"green.png"]; //image2 has retain count 1
[myImageView setImage:image2]; //image1 is released and now has retain count 0, so the memory is freed, image2 now has retain count 2 as it is retained
Since no other objects (other than the UIImageView) are retaining the UIImages, they should be released when they are replaced in the UIImageView and the garbage collector should do its work.
Keep in mind that there is no garbage collector on the iPhone.
You can find more about that here:
StackOverflow:Is garbage collection supported for iPhone applicaions?
(This should actually be a comment to Davids answer but I don't have enough reputation yet)