I am building an app for iOS 5 using ARC and I seem to be having some memory issues. Basically its taking screen-shots of a portion of the display, placing the UIImage in an MSMutableArray and then piecing the screen-shots together for one big image. Now the problem is that after doing this a couple of times the OS closes the application due to high memory usage.
Here is the snippet that pieces the UIImage's together:
UIImage* finalImage = nil;
//join the screenshot images together
UIGraphicsBeginImageContext(CGSizeMake(collage.width, collage.height));
{
int hc = 0;
for(UIImage *img in imageArr)
{
NSLog(#"drawing image at:: %i", hc);
[img drawAtPoint:CGPointMake(0, hc)];
hc+=img.size.height;
img = nil;
}
//NSLog(#"creating finalImage");
finalImage = UIGraphicsGetImageFromCurrentImageContext();
}
UIGraphicsEndImageContext();
//do something with the combined image
//remove all the objects
[imageArr removeAllObjects];
//reset class instance
[self setImageArr: [[NSMutableArray alloc] init]];
Are they any other alternatives that I could use so there isn't so much memory being used? Maybe storing a CGImageRef in the array? Are there any potential memory leaks with the above code?
Any tips, pointers would be greatly appreciated.
Thanks.
[imageArr removeAllObjects]; will remove the objects from array. No need to reset the array again with
[self setImageArr: [[NSMutableArray alloc] init]];
By doing this you are allocating a NSMutableArray object and not releasing it.
Try by removing the line [self setImageArr: [[NSMutableArray alloc] init]];
make sure you alloc and init setImageArr
if (setImageArr == nil){
setImageArr = [[NSMutableArray alloc]init];
}
else
{
[setImageArr removeAllObjects];
}
or use (if you want to init from an existing Array):
NSMutableArray *setImageArr = [[NSMtableArray]initWithArray:arrayOfImages];
Because you say it will have memory issue after doing this a couple of times. Then how about you use NSAutoreleasePool to force system release objects after your method, example below:
#autoreleasepool {
UIImage* finalImage = nil;
//join the screenshot images together
UIGraphicsBeginImageContext(CGSizeMake(collage.width, collage.height));
{
int hc = 0;
for(UIImage *img in imageArr)
{
NSLog(#"drawing image at:: %i", hc);
[img drawAtPoint:CGPointMake(0, hc)];
hc+=img.size.height;
img = nil;
}
finalImage = UIGraphicsGetImageFromCurrentImageContext();
}
UIGraphicsEndImageContext();
//do something with the combined image
//remove all the objects
[imageArr removeAllObjects];
//reset class instance
[self setImageArr: [[NSMutableArray alloc] init]];
}
And I also doubt there is any memory leak problem in your other codes.
Using ARC doesn't meaning without memory leak problem, maybe you store many useless objects in a global variable etc.
Maybe you should use Instruments to monitor the memory to figure out where does the memory go.
Turns out the imageArr is being cleared properly. There appears to be a memory issue somewhere else in the program.
Related
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
my iphone application crash after raising 4 warnings of low memory, instruments is showing no memory leaks but in memory allocation live Bytes goes up to 4.7mb and Over all Bytes goes upto 79.0 MB and application crash at this point
any help will be highly appreciated
for (int i = 0; i<3; i++)
{
UIImage *rendered_image;
UIGraphicsBeginImageContextWithOptions(sub_view.bounds.size, NO, 0.0);
[appdelegate.arrimages removeAllObjects];
[appdelegate.arranimations removeAllObjects];
NSString *oldgroup = [[NSString alloc] init];
NSString *currentgroup = [[NSString alloc] init];
for(int i=0; i<[sub_view.data count]; i++)
{
oldgroup = (i>0) ? [sub_view.group objectAtIndex:(i-1)] : [sub_view.group objectAtIndex:i];
currentgroup = [sub_view.group objectAtIndex:i];
/*
IF DIFFERENT GROUP NAME RECEIVED
1-GET NEW INSTANCE OF IMAGE
2-SAVE PREVIOUS IN ARRAY
*/
if (![oldgroup isEqualToString:currentgroup])
{
rendered_image = UIGraphicsGetImageFromCurrentImageContext();
[self SaveImagesOfAnimation:[self compressImageDownToPhoneScreenSize:rendered_image]];
[appdelegate.arranimations addObject:[sub_view.anim objectAtIndex:i]];
UIGraphicsEndImageContext();
UIGraphicsBeginImageContextWithOptions(sub_view.bounds.size, NO, 0.0);
}
id element = [sub_view.data objectAtIndex:i];
color = [sub_view.fillColor objectAtIndex:i];
[color setFill];
[element fill];
[[UIColor blackColor] setStroke];
[element stroke];
}
rendered_image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self SaveImagesOfAnimation:[self compressImageDownToPhoneScreenSize:rendered_image]];
}
Increasing memory use without a leak implies that you're storing data that you never release, and you're still holding a reference to it.
This usually means that one of the data structures that will grow automatically when you put more data in it, like NSMutableArray, is to blame. They will happily hold all the data you add to them and the memory profiler won't find any leaks since items put in the NSMutableArray are - by definition - never released and not detected as leaks since there's a reference to them from the array.
Edit: For the general way to solve this if you have no obvious places to look, see the comment from #Costique at the top;
Instruments can also show the kind of objects you've allocated and
the exact stack traces, so you should be able to quickly figure it
out.
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];
I'm getting images from UIGetScreenImage() and storing directly in mutable array like:-
image = [UIImage imageWithScreenContents];
[array addObject:image];
[image release];
I've set this code in timer so I cant use UIImagePNGRepresentation() to store as NSData as it reduces the performance. I want to use this array directly after sometime i.e after capturing 1000 images in 100 seconds. When I use the code below:-
UIImage *im = [[UIImage alloc] init];
im = [array objectAtIndex:i];
UIImageWriteToSavedPhotosAlbum(im, nil, nil, nil);**
the application crashes.
And I dont want to use UIImagePNG or JPGRepresentation() in timer as it reduces performance.
My problem is how to use this array so that it is converted into image.
If anybody has idea related to it please share with me.
You don't need to release the image in your first code sample there. [UIImage imageWithScreenContents] returns an autoreleased object.
1. UIImage *im = [[UIImage alloc] init];
2. im = [array objectAtIndex:i];
3. UIImageWriteToSavedPhotosAlbum(im, nil, nil, nil);
Line 1 allocates and initializes a new UIImage object, which never gets released since you overwrite the pointer on line 2. You are leaking an UIImage in each iteration, and you don't even need to init/alloc a new object.
UIImageWriteToSavedPhotosAlbum([array objectAtIndex:i], nil, nil, nil);
Should work just fine.
Also note Carl Norum answer about releasing an autoreleased object.
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.