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)
Related
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.
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.
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
1.UIImageView *img1=[[UIImageView alloc]initwithImage:[UIImage imageNamed:#"1.png"]];
2.UIImageView *img2=[[UIImageView alloc]initwithImage:[UIImage imageNamed:#"2.png"]];
a) img1.image = [UIImage imageNamed:#"2.png"];
b) [img1 setImage:img2];
which way utilizes minimum memory among a and b?why?
if i need to do this multiple times which way you suggest?
b) Because you are creating a reference to an existing object, but they will both point to "2.png" In a) you are creating a new instance of an object which coincidentally happens to point to the same file, but it is allocated as separate memory space.