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
Related
I'm creating an UIImage like this:
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
Am I responsible for releasing this?
No.
Return Value
An autoreleased image
object containing the contents of the
current bitmap graphics context.
(doc link)
Read the documentation here. As it's written there, it returns an autoreleased object. You don't have to release it. If you want to keep it, you need to retain it somehow.
I have an app with multiple UIView subclasses that acts as pages for a UIScrollView. UIViews are moved back and forth to provide a seamless experience to the user. Since the content of the views is rather slow to draw, it's rendered on a single shared CGBitmapContext guarded by locks by NSOperation subclasses - executed one at once in an NSOperationQueue - wrapped up in an UIImage and then used by the main thread to update the content of the views.
-(void)main {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc]init];
if([self isCancelled]) {
return;
}
if(nil == data) {
return;
}
// Buffer is the shared instance of a CG Bitmap Context wrapper class
// data is a dictionary
CGImageRef img = [buffer imageCreateWithData:data];
UIImage * image = [[UIImage alloc]initWithCGImage:img];
CGImageRelease(img);
if([self isCancelled]) {
[image release];
return;
}
NSDictionary * result = [[NSDictionary alloc]initWithObjectsAndKeys:image,#"image",id,#"id",nil];
// target is the instance of the UIView subclass that will use
// the image
[target performSelectorOnMainThread:#selector(updateContentWithData:) withObject:result waitUntilDone:NO];
[result release];
[image release];
[pool release];
}
The updateContentWithData: of the UIView subclass performed on the main thread is just as simple
-(void)updateContentWithData:(NSDictionary *)someData {
NSDictionary * data = [someData retain];
if([[data valueForKey:#"id"]isEqualToString:[self pendingRequestId]]) {
UIImage * image = [data valueForKey:#"image"];
[self setCurrentImage:image];
[self setNeedsDisplay];
}
// If the image has not been retained, it should be released together
// with the dictionary retaining it
[data release];
}
The drawLayer:inContext: method of the subclass will just get the CGImage from the UIImage and use it to update the backing layer or part of it. No retain or release is involved in the process.
The problem is that after a while I run out of memory. The number of the UIViews is static. CGImageRef and UIImage are created, retained and released correctly (or so it seems to me). Instruments does not show any leaks, just the free memory available dip constantly, rise a few times, and then dip even lower until the application is terminated. The app cycles through about 2-300 of the aforementioned pages before that, but I would expect to have the memory usage reach a more or less stable level of used memory after a bunch of pages have been already skimmed at fast speed or, since the images are up to 3MB in size, deplete way earlier.
Any suggestion will be greatly appreciated.
I realize this is an old posting, but in case it helps anybody else .... This looks like a case of memory fragmentation. We have an app that behaves the same way. The amount of memory actually allocated by the app never reaches dangerous levels, but if you look at the amount of resident memory for the app (using VM Tracker snapshots in the Allocations Instrument, or the Activity Monitor Instrument), it climbs inexorably over time until a not-very-large transient spike kills the app.
The app in question is a multi-threaded app that makes tons of transient allocations in a large range of sizes, the timing of which can't be predicted or controlled. Such an app has to be paranoid about releasing unneeded memory allocations, not because they take up too much memory per se, but because they can create holes that prevent larger images from fitting into the allocated blocks. Even smaller allocations that tend to be overlooked are important in fragmentation (granted that the low-level allocator does group allocations by size, which is helpful to an extent). Memory zones are theoretically helpful for addressing fragmentation but pretty hard to make effective, at least in my experience. Also, use custom auto-release pools, or better yet, alloc/init as much as you can, and release as early as possible. The fact that the underlying frameworks are always making their own allocations for caching purposes probably doesn't help.
I wrote some naive code(in the sense that it's synchronous calls) for a tableview that contains thumbnails of images pulled from a url. The code in cellForRowAtIndexPath that pulls the image goes like this:
data = (data == nil)? [[NSData alloc] initWithContentsOfURL:photoThumbPage] : [data initWithContentsOfURL:photoThumbPage];
thumbImg = (thumbImg == nil)? [[UIImage alloc] initWithData:data] : [thumbImg initWithData:data];
I felt that there might be a speedup with not allocating a new nsdata and uiimage each time, so I defined them in my class. Each time I get a thumbnail, I want to check if they are defined, and if they already are, I just initialize them again with different values.
As I am writing this question here, you can already guess that all sorts of bad things happenned:) When I scroll through my tableview, sometimes, two or more entries will share the same image, and I get some errors in console that do not crash my app, but tell me that my jpeg file is corrupted.
Putting aside the fact that each of these requests should be asynchronous, and that I really should be caching these results, can anyone tell me where I might be tripping up? It seems that if cellForRowAtIndexPath is being called for each cell that is visible, and my code is synchronous, there should be no reason why the nsdata and uiimage variables should persist over the calls(they are wiped out by the init methods). I swapped out these member variables and initialized them each time:
NSData *data_local = [[NSData alloc] initWithContentsOfURL:photoThumbPage];
UIImage *thumbImg_local = [[UIImage alloc] initWithData:data];
and it works just fine. Is there any merit in allocating a reusable member variable rather than allocating a new NSData and UIImage each time i want to load a thumbnail? Or is that just a recipe for disaster? Thanks for any comments/help.
The issue is not that you are reusing member variables but that you are reusing the same space in memory. When you call init on data the second time, you are still using the same memory space as before (the space that came from the alloc method). This results in more than one UIImage pointing to the same area of memory, hence the multiple images, and the underlying data changing from underneath each UIImage when a new one is created, hence the error messages about bad jpegs.
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.
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)