iPhone - UIImage imageScaledToSize Memory Issue - iphone

I have done research and tried several times to release the UIImage memory and have been unsuccessful. I saw one other post on the internet where someone else was having this same issue. Everytime imageScaledToSize is called, the ObjectAlloc continues to climb.
In the following code I am pulling a local image from my resource directory and resizing it with some blur. Can someone provide some help on how to release the memory of the UIImages called....scaledImage and labelImage. This is the chunk of code where the iPhone Intruments has shown to have the ObjectAlloc build up. This chunk of code is called several times with an NSTimer.
//Get local image from inside resource
NSString * fileLocation = [[NSBundle mainBundle] pathForResource:imgMain ofType:#"jpg"];
NSData * imageData = [NSData dataWithContentsOfFile:fileLocation];
UIImage * blurMe = [UIImage imageWithData:imageData];
//Resize and blur image
UIImage * scaledImage = [blurMe _imageScaledToSize:CGSizeMake(blurMe.size.width / dblBlurLevel, blurMe.size.width / dblBlurLevel) interpolationQuality:3.0];
UIImage * labelImage = [scaledImage _imageScaledToSize:blurMe.size interpolationQuality:3.0];
imgView.image = labelImage;

You can wrap the calls in an NSAutoreleasePool, where their results will be pooled. Then you can call [pool drain] on that pool and its contents will be released, including the images.
Note however that you will not be able to use the images outside of the NSAutoreleasePool's scope, so you code might want to look something like:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
UIImage * scaledImage = [blurMe _imageScaledToSize:CGSizeMake(blurMe.size.width / dblBlurLevel, blurMe.size.width / dblBlurLevel) interpolationQuality:3.0];
UIImage * labelImage = [scaledImage _imageScaledToSize:blurMe.size interpolationQuality:3.0];
UIImage * imageCopy = [[UIImage alloc] initWithCGImage:labelImage.CGImage]; // Gives a non-autoreleased copy of labelImage
[pool drain]; // deallocates scaledImage and labelImage
imgView.image = imageCopy; // retains imageCopy
UPDATE:
If the above is still giving you problems, please see the solution I posted to this question. The question involves rotating an image 90 degrees instead of scaling it, but the premise is the same (it's just the matrix transformation that is different). Using code like in the answer I posted should give you greater control over your memory management and steer you away from using undocumented APIs like _imageScaledToSize.

Related

Memory management UIImage ImageNamed

I m loading lots of rather large images in my viewcontroller, using
NSUInteger nimages = 0;
for (; ; nimages++) {
NSString *nameOfImage_ = #"someName";
NSString *imageName = [NSString stringWithFormat:#"%#%d.jpg", nameOfImage_, (nimages + 1)];
image = [UIImage imageNamed:imageName];
if (image == nil) {
break;
}
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
//some other stuff....
[imageView release];
}
the usual unloading occurs in - (void)viewDidUnload and - (void)dealloc
with self.image = nil; and [image release];
It seems after a few "loading" and "unloading" the cache still grows to the point of no return!!
:)
and the app crashes...
any ideas??? how do i empty my cache? and where?
thanks
EDIT:
ok this is what i was doing wrong.
Apparently this piece of code fixes the whole caching problem:
image = [[UIImage imageNamed:imageName] autorelease];
with autorelease being the key here.
thanks for the replies...
Thank you all for your suggestions.
Solution:
Used ARC and imageWithContentsOffFile to initialize the Images.
image = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource:imageName ofType:nil]];
And Yes, imageNamed is only good for... well for nothing big...
image = [[UIImage imageNamed:imageName] autorelease];
This is incorrect. In keeping with the memory management rules, you shouldn't be releasing (or autoreleasing) the image because you didn't allocate or retain it. "imageNamed" doesn't contain "alloc", "new", "copy", or "retain".
As some of the other answers explain, you should load your images with a different method if you want more control over the memory they use.
imageNamed is an awful way to load images in reality, it never releases loaded images unless forced and keeps them in the cache forever. You should implement your own, more intelligent cache. A simple NSMutableDictionary gives the same functionality but with more flexibility.
For a more in-depth discussion you can read this: http://www.alexcurylo.com/blog/2009/01/13/imagenamed-is-evil/
Use another method to initialize you image. imageNamed caches.
Instead of using using imageNamed you can use imageWithContentsOfFile:
Or check this article
link 0
link 1

Loading an image from multidimensional array in to UIImageView

I'm loading an image to a custom cell and I build up an array throughout my code from XML which I want to set to my UIImageView on my nib file.
My array gets built up to totalArray and then I do the following in my cellForRowAtIndexPathT:
newObj1=[totalArray objectAtIndex:indexPath.row];
aCell.lblRouteText.text = newObj1.routeText;
And the following to load my image works (static):
UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://imageurl.jpg"]]];
aCell.imgRoute.image = image;
However, when I try to put my array in using the following:
UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[newObj1.routeImage]]];
I get an error of identifier expected
Any tips?
Tom
You're writing [newObj1.routeImage] where you just need newObj1.routeImage, i.e. your final line should probably read:
UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:newObj1.routeImage]];
When you use square brackets (and not in the context of a C array), you should always have something of the form [x y], which means 'send the message y to the receiver x'.
Note on threading: be careful about using [NSData dataWithContentsOfURL:], because it blocks while the URL contents are fetched. If you call this on the main thread, you will block your UI and the app will be unresponsive.
(Btw, if you're posting error messages, it's best to copy and paste the full actual error line(s) from the console if possible -- it maximises our chance of understanding the problem.)

iphone UIImage memory leaks

I'm implementing an image browser, using a UIScrollView. Due to memory costranints, I've to implement image dynamic loading (I don't want use CATiled Layers because it forces user remaining waiting to load every tile).
I've tried in a coupe of ways:
- (UIImageView*) ldPageView{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Top-level pool
NSError *error;
NSData *imData = [NSData dataWithContentsOfURL:ldPageRef options:NSDataReadingUncached error:&error];
UIImage *im = [[UIImage alloc] initWithData:imData];
ldView = [[UIImageView alloc] initWithImage:im] ;
[ldView setFrame:pageRect];
[pool release]; // Release the objects in the pool.
return ldView;
}
And even in this way
- (UIImageView*) ldPageView{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Top-level pool
CGDataProviderRef provider = CGDataProviderCreateWithURL ((CFURLRef)ldPageRef);
CGImageRef d = CGImageCreateWithJPEGDataProvider(provider,nil, true,kCGRenderingIntentDefault);
UIImage *im = [[UIImage alloc] initWithCGImage:d];
ldView = [[[UIImageView alloc] initWithImage:im] autorelease];
[im release];
CGDataProviderRelease(provider);
CGImageRelease(d);
[ldView setFrame:pageRect];
[pool release]; // Release the objects in the pool.
return ldView;
}
But every time I try it both on simulator and on iPad, memory explodes. I've runned my code with Instruments and no leak is reported. ldView is an istance variable and it is deallocated togheter with ldPageRef on object dealloc (which is called for sure).
I've also tried setting NSURLCache sharedCache to nil or to zero, but it is still happening.
I've read Memory management guide, but everythimg seems ok to me.
Please help
Try using
UIImage *im = [UIImage imageWithData:imData];
rather than
UIImage *im = [[UIImage alloc] initWithData:imData];
Always avoid allocs if possible otherwise you must ensure that you manually release the object.
More than likely it is how you are creating your UIImage. Try creating your image as such..
[UIImage imageWithData:imData];
instead of
[[UIImage alloc] initWithData:imData];
This will return a autoreleased object(it is a class method) so that you will not have to try to release it yourself later.
You are never releasing your alloc'd objects. You need to change:
[[UIImage alloc] initWithData:imData];
[[[UIImageView alloc] initWithImage:im];
to:
[[[UIImage alloc] initWithData:imData] autorelease];
[[[UIImageView alloc] initWithImage:im] autorelease] ;
Indeed I have found a memory leak in UIImageView. You just never pay any attention to it, since you may open images from the App package all the time, and these images are being cached by iOS.
But if you download a number of images from the network (say 40 iPhone-camera photos), save them to the documents and open them over and over again in a sub view controller, the memory leak applies. You do not need to have 40 different images, it is enough to load one image again and again.
Your test app is with ARC disabled and an image is being loaded from file and displayed in a sub view controller, every time that controller is being pushed.
In your sub view controller you'll create an UIImageView and assign the UIImage object to the image view's .image property. When you leave the sub view controller, you release the image view correctly. On an iPhone 4s your app won't open more than 80 images. Mine crashed at around 70 images (with iOS 6.1). And have a look on the intruments app, before the crash. The memory is full of CFMalloc blocks.
The solution I found is simple: Set the image property to nil, before you release the image view. Again, look at the instruments app. It now looks like what you'd expect and your app does no longer crash.
I think the same leak applies to whatever the UIWebView uses to display images and Apple doesn't know about it.

Why is this leaking memory? UIImage `cellForRowAtIndexPath:`

Instruments' Leaks tells me that this UIImage is leaking:
UIImage *image = [[UIImage alloc] initWithContentsOfFile:[imagesPath stringByAppendingPathComponent:[NSString stringWithFormat:#"/%#.png", [postsArrayID objectAtIndex:indexPath.row]]]];
// If image contains anything, set cellImage to image. If image is empty, try one more time or use noImage.png, set in IB
if (image != nil){
// If image != nil, set cellImage to that image
cell.cellImage.image = image;
}
image = nil;
[image release];
(class cell (custom table view cell) also releases cellImage in dealloc method).
I haven't got a clue of why it's leaking, but it certainly is.
The images gets loaded multiple times in a cellForRowAtIndexPath:-method. The first three cells' image does not leak (130px high, all the space avaliable).
Leaks gives me no other info than that a UIImage allocated here in the code leaks.
Can you help me figure it out? Thanks :)
The code you have there would be correct if image was a #property. You can release a #property by doing self.property = nil because of the way the setter works. The setter releases the old object and sets the ivar to the value. In order to fix this you would need to put [image release] first. What is happening here is that you set image to nil and then you are essentially doing [nil release]. The old image is just floating around somewhere. So to fix this do the following:
[image release];
image = nil;
You are setting it to nil before calling release. So you are releasing nil instead of image.
Change from:
image = nil;
[image release];
to:
[image release];
image = nil;
Little bit code modification
UIImage *image = [[UIImage alloc] initWithContentsOfFile:[imagesPath
stringByAppendingPathComponent:[NSString stringWithFormat:#"/%#.png",
[postsArrayID objectAtIndex:indexPath.row]]]];
if (image){
cell.cellImage.image = image;
[image release];

ImageIO initImageJPEG instances getting allocated and never released

Im developing an app for an iPhone and I found that the following code is causing the memory allocation to increment.
-(UIImage *)createRecipeCardImage:(Process *)objectTBD atIndex:(int)indx
{
[objectTBD retain];
// bringing the image for the background
UIImage *rCard = [UIImage imageNamed:#"card_bg.png"];
CGRect frame = CGRectMake(00.0f, 80.0f, 330.0f, 330.0f);
// creating he UIImage view to contain the recipe's data
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.image = rCard;
[rCard release];
imageView.userInteractionEnabled = YES;
float titleLabelWidth = 150.0;
float leftGutter = 5.0;
float titleYPos = 25.0;
float space = 3.0;
float leftYPos = 0;
// locating Title label
float currentHeight = [self calculateHeightOfTextFromWidth:objectTBD.Title :titleFont :titleLabelWidth :UILineBreakModeWordWrap];
UILabel *cardTitle = [[UILabel alloc]initWithFrame:CGRectMake(leftGutter, titleYPos, titleLabelWidth, currentHeight)];
cardTitle.lineBreakMode = UILineBreakModeWordWrap;
cardTitle.numberOfLines = 0;
cardTitle.font = titleFont;
cardTitle.text = objectTBD.Title;
cardTitle.backgroundColor = [UIColor clearColor];
[imageView addSubview:cardTitle];
[cardTitle release];
leftYPos = titleYPos + currentHeight + space;
// locating brown line
UIView *brownLine = [[UIView alloc] initWithFrame:CGRectMake(5.0, leftYPos, 150.0, 2.0)];
brownLine.backgroundColor = [UIColor colorWithRed:0.647 green:0.341 blue:0.122 alpha:1.0];
[imageView addSubview:brownLine];
[brownLine release];
leftYPos = leftYPos + 2 + space + space + space;
// creating the imageView to place the image
UIImageView *processPhoto = [[UIImageView alloc] initWithFrame:CGRectMake(leftGutter, leftYPos, 150, 150)];
if((uniqueIndex == indx) && (uniqueImg.imageData != nil))
{
if([uniqueImg.rcpIden isEqualToString:objectTBD.iden])
{
objectTBD.imageData = [NSString stringWithFormat:#"%#", uniqueImg.imageData];
[recipesFound replaceObjectAtIndex:indx withObject:objectTBD];
NSData * imageData = [NSData dataFromBase64String:objectTBD.imageData];
UIImage *rcpImage = [[UIImage alloc] initWithData:imageData];
[imageData release];
processPhoto.image = rcpImage;
[rcpImage release];
}
}
else if(objectTBD.imageData != nil)
{
NSData * imageData = [NSData dataFromBase64String:objectTBD.imageData];
UIImage *rcpImage = [[UIImage alloc] initWithData:imageData];
processPhoto.image = rcpImage;
[rcpImage release];
[decodedBigImageDataPointers addObject:imageData];
}
else
{
UIImage * rcpImage = [UIImage imageNamed:#"default_recipe_img.png"];
processPhoto.image = rcpImage;
[rcpImage release];
}
NSlog(#" Process Photo Retain Count %i", [processPhoto retainCount]); // this prints a 1
[imageView addSubview:processPhoto];
NSlog(#" Process Photo Retain Count %i", [processPhoto retainCount]); // this prints a 2!!!!
//[processPhoto release]; // this line causes an error :(
// converting the UIImageView into a UIImage
UIGraphicsBeginImageContext(imageView.bounds.size);
[imageView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[objectTBD release];
for(UIView *eachSubview in imageView.subviews)
{
[eachSubview removeFromSuperview];
NSLog(#"each subview retainCount %i despues", [eachSubview retainCount]);
// here I find that the processPhoto view has a retain count of 2 (all other views have their retain count in 1)
}
return viewImage;
}
When I checked at the instruments object allocation I found that the "GeneralBlock-9216" growing up.
Breaking down the row I found that every time I call this code, one instance of:
2 0x5083800 00:18.534 ImageIO initImageJPEG
is being allocated. Checking the call stack, the following line is highlighted:
UIImage * objImage = [UIImage imageWithData:imageData];
Any help to find what the error is?
As TechZen said, the imageWithXXX: methods cache the image inside of them while you run the program (though you release the instances after using). I recommend initWithXXX: and release API sets instead of imageWithXXX:.
Well, if you embed several debug log on your source code, check how many times is it called, and check the retain count of the instances.
As far as I can explain, that is all.
I hope you will solve the problem.
Does anyone have an answer for this? It's tearing me apart trying to figure out why this image information keeps lingering. I've tried every solution.
The situation:
Images get downloaded and stored to the device, then loaded with imageWithContentsOfFile (or even initWithContentsOfFile, which doesn't help either). When the view goes away, the images don't, but they don't show up as leaks, they're just this initImageJPEG Malloc 9.00 KB that never goes away and keeps ramping up.
UPDATE: I believe I've figured this out: Check to make sure everything is actually getting dealloc'd when you're releasing whatever the parents (and/or grandparents) and etc of the images are. If the parents don't get deallocated, they never let go of their children images, and whatever data was in those images sticks around. So check retain counts of parent objects and make sure that everything's going away all the way up whenever you release the view at the top.
A good way to check for this is to put NSLogs into custom classes' dealloc methods. If they never show up, that object isn't going away, even though the reference to it might, and it (and whatever its subviews and properties are) will never ever disappear. In the case of images, this means a pretty sizable allocation every time that object is generated and never deallocated. It might not show up in leaks, especially if the parent of the topmost object you're thinking you're releasing but actually aren't persists and doesn't itself ever deallocate.
I hope this helps. It'll be useful to take some time to read through your code with a fine-toothed comb to make sure you're allocating and releasing things properly. (Search for "alloc]", start at the top of the file, and work your way down to make sure you're releasing and that the release isn't inside of some if() or something.)
Also, running "Build and Analyze" might lock up your machine for a bit, but its results can be really helpful.
Good luck!
I think you're seeing UIImage cacheing images. There used there used to be a method something like initWithData:cache that let you turn the cacheing off. Now I think the system always caches the images automatically behind the scenes even after you've deallocted the specific instances.
I don't think its an error on your part. I think it's the system keeping data around in the OpenGl subsystem. Unless it causes a major leak, I don't think it is a problem.