When I analyze the following code with Instruments, it reports a leak on variable imageName:
//loadImagesFromPotatoesIndexesArray
-(void) loadImagesFromPotatoesIndexesArray{
//Load Textures from Disk
textures = [[NSMutableArray alloc] init];
//NSArray *masks = [[NSArray alloc] initWithArray:mainDelegate.masksArray];
for (int i = 0;i<[potatoesIndexesArray count];i++){
int imageNumber = [[potatoesIndexesArray objectAtIndex:i]intValue];
NSString *imageName = [[NSString alloc] initWithFormat:#"texture%d",imageNumber];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:imageName ofType:#"png"]];
NSArray *pics = [[NSArray alloc] initWithObjects:
[self maskImage:image withMask:[mainDelegate.masksArray objectAtIndex:i]],
[self maskImage:image withMask:[mainDelegate.masksArray objectAtIndex:i]],
imageName,
nil];
[textures addObject:pics];
[image release];
[imageName release];
[pics release];
}
}
[potatoesIndexesArray count] = 16, so I've got 16 times that NSCFString leaking ... But to me the code is respecting memory management ... obviously not!!!
What did I do wrong?
You never release the 'textures' array. It's still holding everything.
How often is loadImagesFromPotatoesIndexesArray called in your code? If is called more than once, all of the values in the original array will be leaked, since you don't properly release textures before replacing it with a new array.
If it is being called more than once, this should do the trick:
// load textures from disk
[textures removeAllObjects];
//NSArray *masks = [[NSArray ...
for (int i=0; ...
If think that when you add imageName in your pics array it retain it ;-)
(I think it answer to your question)
But, why are you doing a alloc here ?
why not doing something like
[ NSString stringWithFormat:#"" ] ?
Good Luck !
This is a complicated issue. You alloc the imageName, so the retainCount is 1, then you add it into an array, the retain count is 2, when you release the imageName, the retain Count is 1 again. Then if you also release the pics array, everything will be fine. But your pics array is added into textures, then the pics is released, so your pics retainCount is still 1. And your imageName is leaked. But, if you release the textures array, everything will be fine
NSString *imageName = [[NSString alloc] initWithFormat:#"texture%d",imageNumber];
NSArray *pics = [[NSArray alloc] initWithObjects:
[self maskImage:image withMask:[mainDelegate.masksArray objectAtIndex:i]],
[self maskImage:image withMask:[mainDelegate.masksArray objectAtIndex:i]],
imageName,
nil];
[imageName release];
Related
I have 100 images in my resource bundle named like image1.jpg,image2.jpg.
Basically what i am trying to do is create path names to those images dynamically inside a for loop.
While testing in simulator,the images loaded fine and the app did not crash.But while testing the app with instruments i was shocked to see the heavy memory leak that was happening while i was creating the path1 object.
I am pasting the entire method here for reference
- (id)init {
self = [super init];
if (self) {
self.arrayImages = [[[NSMutableArray alloc] init] autorelease];
for(int i=1 ; i<100 ; i++){
NSString *str = [NSString stringWithFormat:#"Century%d",i];
NSString *path1 = [[NSBundle mainBundle] pathForResource:str ofType:#"jpg"];
[self.arrayImages addObject:path1];
}
}
return self;
}
As i have not made use of any alloc inside the loop i dont have any ownership and hence no right to release the object.What is the reason for this memory leak??
Kindly explain the problem and provide the necessary solution in order to fix it..
As always,any help is highly appreciated..
arrayImages is retaining path1, and so if you do not release arrayImages it will leak. How are you creating arrayImages, and are you releasing it anywhere?
Edited based on comments:
Make sure you release arrayImages in your -dealloc method like so: [arrayImages release]; (note the lack of self).
There is no leak in the code you've shown.
There are (at least) two possibilities:
You have a leak in code you didn't paste into your question
Everything is fine and Instruments gave you a false-positive
Your loop will create a lot of autoreleased variables. These won't be deallocated until after the loop has finished, but that's how it's supposed to work.
The reason for the leak would be this line right here:
NSString *str = [NSString stringWithFormat:#"Century%d",i];
By using convenience methods in Objective-C, what happens in the background is the following:
NSString *str = [[[NSString alloc] initWithFormat:#"Century%d", i] autorelease];
Not using alloc/init to create a weak reference is a misconception. You are always the owner of a created object, no matter how you create it. The convenience method simply does the alloc/init and autoreleases it for you.
Here's what I would suggest you do to avoid leaking memory:
- (id)init {
self = [super init];
if (self) {
self.arrayImages = [[[NSMutableArray alloc] init] autorelease];
NSAutoreleasePool *tmpPool = [[NSAutoreleasePool alloc] init];
for(int i = 1 ; i < 100 ; i++) {
NSString *str = [NSString stringWithFormat:#"Century%d",i];
NSString *path1 = [[NSString alloc] initWithString:[[NSBundle mainBundle] pathForResource:str ofType:#"jpg"]];
[self.arrayImages addObject:path1];
[path1 release];
}
[tmpPool drain];
}
return self;
}
Let me know if this works better for you.
-EDIT- Allocating the path1 object and releasing it after adding to arrayImages.
There are multiple memory leaks in this section of my code. Specifically with these arrays: PlaylistItem, PlaylistItemID and PlaylistItemLength. The problem is that I can't successfully release the arrays. When I attempt to use insert [xxxx release]; anywhere in this code, the app locks up. It's driving me absolutely nurtz!
-(void)configureCueSet {
MPMediaQuery *myPlaylistsQuery = [MPMediaQuery playlistsQuery];
NSArray *playlists = [myPlaylistsQuery collections];
//Get # of items in a playlist and names -------------------------------------
NSArray *songs;
for (MPMediaPlaylist *playlist in playlists) {
NSString *playListItem = [playlist valueForProperty: MPMediaPlaylistPropertyName];
if ([playListItem isEqualToString: savedLastSelectedPlaylist]){
songs = [playlist items];
}
}
PlaylistItem = [[NSMutableArray alloc] init];
PlaylistItemID = [[NSMutableArray alloc] init];
PlaylistItemLength = [[NSMutableArray alloc] init];
for (MPMediaItem *song in songs) {
[PlaylistItem addObject:[song valueForProperty: MPMediaItemPropertyTitle]];
[PlaylistItemID addObject:[song valueForProperty: MPMediaItemPropertyPersistentID]];
[PlaylistItemLength addObject:[song valueForProperty: MPMediaItemPropertyPlaybackDuration]];
}
}
Does that method get called multiple times? If so, your leak likely occurs on that assignment. You'd want:
[PlayListItem release];
PlaylistItem = [[NSMutableArray alloc] init];
[PlayListItemID release];
PlaylistItemID = [[NSMutableArray alloc] init];
[PlaylistItemLength release];
PlaylistItemLength = [[NSMutableArray alloc] init];
If you don't release what was there before, then you'll get a leak.
Attempting to insert [xxx release] would release the contents, not the arrays. The application crashes because with that you are deallocating the object which you are about to add to the array. According to the documentation (here), the values in an NSArray are automatically retained, and will be released as soon as the array is dealloc'ed. So, if you want to release any of those arrays, simply type [PlaylistItem release].
I have got an strange problem but I am quite sure, for you it is not. If it is so, please help me or explain.
I have an array of 39 UIImages which will be used for animating UIImageView. and after playing the animation, memory does not release.
- (void)viewDidLoad {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSMutableArray* actionArray = [[NSMutableArray alloc]initWithCapacity:38];
for (int i=1; i<=39; i++) {
NSString* path = [[NSBundle mainBundle]pathForResource:[NSString stringWithFormat:#"bg_Level1_%.4d", i] ofType:#"png"];
UIImage *image = [[UIImage alloc]initWithContentsOfFile:path];
[path release];
[actionArray addObject:image];
[image release];
NSLog(#"path rc %d image %d", [path retainCount], [image retainCount]);
}
imageView.animationImages = actionArray;
imageView.animationDuration = 4.0;
imageView.animationRepeatCount = 1;
[imageView startAnimating];
NSLog(#"RetainCount ActArr %d ImageView %d", [actionArray retainCount], [imageView retainCount]);
[actionArray release];
[pool drain];
[imageView release];
[self performSelector:#selector(playAnimation2) withObject:self afterDelay:5.0];
[super viewDidLoad];
}
-(void)playAnimation2{
if ([imageView isAnimating]) {
[imageView stopAnimating];
}
NSLog(#"RetainCount ImageView %d",[imageView retainCount]);
}
there is no leaks discovered but the amount of memory allocated still is 24 mb.
or it is not so necessary ?
EDIT:
Sorry, my fault.
Due to many changes of my code I've got lost in it. After analysing I get it!
Thanks a lot!
The correct Code is:
- (void)viewDidLoad {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSMutableArray* actionArray = [[NSMutableArray alloc]initWithCapacity:38];
for (int i=1; i<=39; i++) {
NSString* path = [[NSBundle mainBundle]pathForResource:[NSString stringWithFormat:#"bg_Level1_%.4d", i] ofType:#"png"];
UIImage *image = [[UIImage alloc]initWithContentsOfFile:path];
// [path release];
[actionArray addObject:image];
[image release];
NSLog(#"path rc %d image %d", [path retainCount], [image retainCount]);
}
imageView.animationImages = actionArray;
imageView.animationDuration = 4.0;
imageView.animationRepeatCount = 1;
[imageView startAnimating];
NSLog(#"RetainCount ActArr %d ImageView %d", [actionArray retainCount], [imageView retainCount]);
[actionArray release];
[pool drain];
[self performSelector:#selector(playAnimation2) withObject:self afterDelay:5.0];
[super viewDidLoad];
}
-(void)playAnimation2{
if ([imageView isAnimating]) {
[imageView stopAnimating];
}
[imageView removeFromSuperview];
imageView.animationImages = nil;
NSLog(#"RetainCount ImageView %d",[imageView retainCount]);
}
The problem was with [path release];
There are quite a few things wrong with this code. It is a wonder it runs at all.
First:
Do not use -retainCount.
The absolute retain count of an object is meaningless.
You should call release exactly same number of times that you caused the object to be retained. No less (unless you like leaks) and, certainly, no more (unless you like crashes).
See the Memory Management Guidelines for full details.
Now, some of the problems:
You asked if you need to empty the array and then said array=nil; ... doesn't work. Setting an array reference to nil doesn't do anything to the array; doesn't release it or empty it.
you are overreleasing path
you are loading a series of images, sticking 'em in an array, and there isn't anything in that code that is releasing the array (or emptying it). That your app continues to use the memory and shows no leaks sound like correct behavior.
Why are you releasing imageView? There is nothing in that code implying that you retained it and, even if it were retained via some other mechanism (IB?), that would be a really odd place to release it.
I think you need to remove all the objects from the array for it to work.
Try deleting the objects from the array, and then the array once you're finished with them.
You are releasing imageView without retaining it anywhere, so there is probably something else wrong with you code. It seems however, that imageView is retained by another object ( most likely the superview), which keeps it in memory. This will also keep the array of images in memory.
If you don't need imageView anymore, remove it from its parent with [imageView removeFromSuperview];
As we already solved here: releasing problems
Set imageView.animationImages = nil;
Also, you need to remove the [path release]; or it will crash.
The Memory Management Guide is a vital place to start with.
Edit Never rely on the retainCount property!
Edit 2 Releasing imageView in that method is clearly a bug as well!
where to dealloc/ release my NS-mutable array a1 ??
see this
- (void)viewDidLoad {
[NSThread detachNewThreadSelector:#selector(loadImage) toTarget:self withObject:nil];
}
- (void) loadImage
{
NSLog(#" THREAD METHOD");
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSUserDefaults *imgg = [NSUserDefaults standardUserDefaults];
myimg= [imgg stringForKey:#"keyToimg"];
NSLog(#"RES image sssssssss is = %#",myimg);
a1 = [[NSMutableArray alloc] init];
[a1 addObjectsFromArray:[myimg componentsSeparatedByString:#"\n\t"]];
//[a1 removeAllObjects];
////
//[myimg release];
[pool release];
}
and in table cell of secition 3 i am displaying image
switch(indexPath.section)
{
NSString *urlE=[a1 objectAtIndex:1];
NSLog(#"url is %#",urlE);
NSData *backgroundData = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlE]];
image = [UIImage imageWithData:backgroundData];
myImageView= [[UIImageView alloc] initWithImage:image];
[myImageView setUserInteractionEnabled:YES];
CGRect rect=CGRectMake(20 ,10, 270, 180);
myImageView.frame = rect;
myImageView.tag = i;
[cell.contentView addSubview:myImageView];
}
and based on tap images are changing
pragma mark working image tap
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#" life count %d",[myimg retainCount]);
NSLog(#" life array count %d",[a1 retainCount]);
//NSLog(#" GITSHffffffffffffffffffffffffffffffC");
NSUInteger sections = [indexPath section];
//NSLog(#"row is %d",sections);
if (sections == 3)
{ //Its either 1 or 0 I don't remember, it's been a while since I did some tableview
if(tap<[a1 count]-1) {
NSLog(#" life array count %d",[a1 retainCount]);
tap++;
NSString *sa=[a1 objectAtIndex:tap];
//////////////////////
image= [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat: sa,[a1 objectAtIndex:tap ]]]]];
NSData *imageData = [NSData dataWithData:UIImageJPEGRepresentation(image, 1.0)];
myImageView.image = image;
//[myimg release];
//[a1 release];
}
else {
tap=1;
//[myimg release];
//[a1 release];
}
}
//[a1 release];
}
so where should i release my a1 and myimg
a1 will never be released using this code.
You should put it on a member variable or add autorelease after init.
By the way, your myImageView should be released after you add it to the cell view.
It is possible because of the retain/release logic: when you alloc the myImageView the retain count is +1, once you add it to cell view,it is now +2, you should then release it so that the retain comes back to +1 and then when cell view will be further deallocated, it will decrement the retain count to 0.
The same logic for the variable image in the last function
Regards
Meir assayag
Instead of :
a1 = [[NSMutableArray alloc] init];
[a1 addObjectsFromArray:[myimg componentsSeparatedByString:#"\n\t"]];
Consider:
a1 = [NSMutableArray arrayWithArray:[myimg componentsSeparatedByString:#"\n\t"]];
That'll initialize your a1 with an autoreleased NSMutableArray object, and then you don't have to worry about manually releasing it.
The thing I don't know is whether your [pool release] will release it, but... I'd really prefer you NOT put that business in a background thread, but rather use asynchronous network methods to get your image data.
By the way, as I was learning iPhone development, I went through three or four levels of "aha moments" about backgrounded networking. One of them had to do with running selectors on background threads. That lasted about a week until I discovered ASIHttpRequest, which is how I do it now. MUCH simpler way to put network interactions in the background without having to mess with threading or any of that nonsense. See http://allseeing-i.com/ASIHTTPRequest/
If you look at my answers, every time HTTP client networking comes up I recommend ASI. I really don't mean to be a shill for it--it's just made my life so much easier I think everyone needs to know about it.
I am changing the image in UIImageView based on accelerometer input. The images are stored in an array.
The application runs fine for a while and then crashes.
warning: check_safe_call: could not restore current frame
I am not using "UIImage ImageNamed" method when populating the array.
The total size of all images is around 12 Mb. But individual images are very light (<100 kb) and i am using only one image at a time from the array.
Just in case there are any autoreleases, I have allocated a NSAutoreleasePool in view did load and am draining it in the didReceiveMemoryWarning method (which does get called 2, 3 times before the app crashes?).
Following is the code that creates images array:
//create autorelease pool as we are creating many autoreleased objects
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *finalarr = [[NSMutableArray alloc] initWithCapacity:9];
NSLog(#"start loading");
for(int y = 0; y < 100; y+=10)
{
NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:10];
for(int x = 0; x < 10; x++)
{
NSString *imageName = [[NSString alloc] initWithFormat:#"0%d",y + x];
UIImage *img = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:imageName ofType:#"png"]];
[arr addObject:img];
[imageName release];
[img release];
}
[finalarr addObject:arr];
[arr release];
}
NSLog(#"done loading");
// Override point for customization after app launch
viewController.imagesArray = finalarr;
[finalarr release];
//retain the array of images
[viewController.imagesArray retain];
//release the aurtorelease pool to free memory taken up by objects enqued for release
[pool release];
As the app runs smoothly for a while, which means array creation is definitely not the problem.
After this the following method is called from [accelerometer didAccelerate]
-(BOOL)setImageForX:(int)x andY:(int)y
{
[self.imageView setImage:(UIImage*)[(NSArray*)[self.imagesArray objectAtIndex:y] objectAtIndex:x]];
return TRUE;
}
So the only code being executed is the "UIImageView setImage". But no objects are being created here.
Please let me know if the code seems to have any leaks or i am doing something wrong.
Thanks,
Swapnil
NSAutoreleasePool should be allocated and released in the same method. Allocating it in one place and releasing it in another is usually very bad (the example code you pasted uses it correctly however)
Loading images into memory will decompress them; a 100KB compressed PNG could easily be 2MB uncompressed.
You also seem to have an extra retain call that shouldn't be there: [viewController.imagesArray retain]; (I assume the imagesArray property is marked copy or retain and not assign)