Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
372
25
Hi all,

Having recently taken up app development, i've reached the stage where my app does what I want it to do, and I'm trying to fix any bugs that exist. Unfortunately, I've got a memory leak...

I have a nav bar in a tab app, each row of the nav bar's table view contains a link to a new view, which all contain a row of images within a scrollview. I'm pretty sure this is where my problem is:

Code:
- (void)viewDidLoad
{
self.view.backgroundColor = [UIColor blackColor];
// 1. setup the scrollview for multiple images and add it to the view controller
//
// note: the following can be done in Interface Builder, but we show this in code for clarity
// [scrollView1 setBackgroundColor:[UIColor blackColor]];
[scrollView1 setCanCancelContentTouches:NO];
scrollView1.indicatorStyle = UIScrollViewIndicatorStyleWhite;
scrollView1.clipsToBounds = YES; // default is NO, we want to restrict drawing within our scrollview
scrollView1.scrollEnabled = YES;
scrollView1.pagingEnabled = YES;
scrollView1.bounces = YES;
scrollView1.directionalLockEnabled = YES;
scrollView1.maximumZoomScale = 1.0;
scrollView1.minimumZoomScale = 1.0;
scrollView1.multipleTouchEnabled = YES;
scrollView1.delegate = self;


// load all the images from our bundle and add them to the scroll view
NSUInteger i;
for (i = 1; i <= kNumImages; i++)
{
NSString *imageName = [NSString stringWithFormat:@"image%d.png", i];
UIImage *image = [UIImage imageNamed:imageName];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
^^ i'm pretty sure the problem is that the image(x).png is not being released, but I can't figure out where I can release it - should i add it to the .h file so i can dealloc it at the end?
Code:
// setup each frame to a default height and width, it will be properly placed when we call "updateScrollList"
CGRect rect = imageView.frame;
rect.size.height = kScrollObjHeight;
rect.size.width = kScrollObjWidth;
imageView.frame = rect;
imageView.tag = i; // tag our images for later use when we place them in serial fashion
[scrollView1 addSubview:imageView];
[imageView release];

}
[self layoutScrollImages];
}
 

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
372
25
That code looks fine. How are you determining there's a leak?

Thanks for your response! other than the crash, when I run with the Leaks performance tool, it highlights the line
Code:
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
as leaking the number of UIImageViews that there are number of images on that particular view when the view opens...

The only other thing I can think of is that it's not closing the views properly... here's how i'm opening them in my tableviewcontroller:

Code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
 
 
 NSInteger row = [indexPath row];

 
   if([[booksArray objectAtIndex:row] isEqual:@"Contents"])
   {
    AboutViewController *aBookDetail = [[AboutViewController alloc] initWithNibName:@"AboutView" bundle:nil];
    self.aboutViewController = aBookDetail;
    self.hidesBottomBarWhenPushed = YES;
    [aBookDetail release];
    aboutViewController.title = [NSString stringWithFormat:@"%@", [booksArray objectAtIndex:row]];
    [self.navigationController pushViewController:aboutViewController animated:YES];
   } 
   else
    if([[booksArray objectAtIndex:row] isEqual:@"Introduction"])
    {
     IntroductionView *bBookDetail = [[IntroductionView alloc] initWithNibName:@"IntroductionView" bundle:nil];
     self.introductionView = bBookDetail;
     self.hidesBottomBarWhenPushed = YES;
     [bBookDetail release];
     introductionView.title = [NSString stringWithFormat:@"%@", [booksArray objectAtIndex:row]];
     [self.navigationController pushViewController:introductionView animated:YES];
     }
then it uses the navigation controller back button to go back to the table view... each view has a release in the dealloc though I think maybe there could be some statement I should make after pushViewController that releases the view when the back button is pressed?

It doesn't crash the simulator, just the device - i guess because it only just exceeds the memory limit and the simulator has more available resources...
 

robbieduncan

Moderator emeritus
Jul 24, 2002
25,611
893
Harrogate
In the dealloc method for the view controller that has been removed from the screen you should release all object that you currently own. So anything that you create with alloc/init that has not yet been released should be released there.
 

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
372
25
In the dealloc method for the view controller that has been removed from the screen you should release all object that you currently own. So anything that you create with alloc/init that has not yet been released should be released there.

I've got that much but I'm just not sure what i'm missing :confused: the imageView is released in the same section of code, I've tried making it a class and releasing it in the dealloc as well and I've tried just releasing UIImageView/UIImage in the dealloc but I can't figure out how all the images are staying stuck in the cache - I don't know what to release!

*although I should mention the only thing in the dealloc at the moment is scrollView1
 

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
372
25
bump:

I think perhaps I need to set 'image' from this code
Code:
NSString *imageName = [NSString stringWithFormat:@"image%d.png", i];
UIImage *image = [UIImage imageNamed:imageName];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
to nil, but if i do it in viewDidLoad it breaks and if I set it outside of viewDidLoad it gives me a 'not declared' error...

Totally stuck here! :(
 

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
372
25
Why do you think that?

oooh, this one is attracting all the demi-gods :D

well, read this:

http://www.alexcurylo.com/blog/2009/01/13/imagenamed-is-evil/

I don't have a problem with the imageNamed on any particular view, I can open any one view as many times as I like without crashing the app, but for some reason the images aren't being released when the view changes... in that article it sounds as though all their images were in the same place so I figure I must have missed something...
 

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
372
25
imageNamed: caches images. Why would you expect them to be released?

Well I got to the point of realising they weren't being released and started trying to figure out how to release them before closing the view. Which is how I got to figuring, perhaps setting image to nil would do it... I just can't figure out how to do that outside of the viewDidLoad section :/

*edit: or inside the viewDidLoad section, specifying that it doesn't happen until the view closes...
 

dejo

Moderator emeritus
Sep 2, 2004
15,982
452
The Centennial State
If you want to be able to control when the images are released (i.e. you don't want them to be cached), then you shouldn't use imageNamed:. Consider using initWithContentsOfFile: instead. Then you can just release them once you've used them for the initWithImage:.
 

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
372
25
If you want to be able to control when the images are released (i.e. you don't want them to be cached), then you shouldn't use imageNamed:. Consider using initWithContentsOfFile: instead. Then you can just release them once you've used them for the initWithImage:.

Thanks for the tip! I've now swapped
Code:
NSString *imageName = [NSString stringWithFormat:@"image%d.png", i];
UIImage *image = [UIImage imageNamed:imageName];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
with
Code:
NSString *path = [[NSBundle mainBundle] pathForResource:[NSString 
                 stringWithFormat:@"image%d",i] ofType:@"png"]; 
        UIImage *image = [UIImage imageWithContentsOfFile:path]; 
        UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
but I get the same problem - what do I need to release, and where? :S
 

dejo

Moderator emeritus
Sep 2, 2004
15,982
452
The Centennial State
Code:
NSString *path = [[NSBundle mainBundle] pathForResource:[NSString 
                 stringWithFormat:@"image%d",i] ofType:@"png"]; 
UIImage *image = [B][COLOR="Red"][[UIImage alloc] initWithContentsOfFile:[/COLOR][/B]path]; 
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[B][image release];
...
[scrollView1 addSubview:imageView];
[imageView release];[/B]
That should work.
 

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
372
25
That should work.

unfortunately that causes the view to open properly once, but crash the app if you choose to view the same view from the table again :/

edit: Sorry! Missed the UIImage alloc - that'll probably get it working - thank you :D i've been fighting this problem for about half a week ^.^

double edit: damnit once I put that in, it crashes when I select the table cell to open the view :'(
 

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
372
25
Where, in terms of the code, is it crashing?

The console gives me -[UIImage imageWithContentsOfFile:]: unrecognized selector sent to instance 0x4249d80
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[UIImage imageWithContentsOfFile:]: unrecognized selector sent to instance 0x4249d80
Stack:
(Stack numbers)

in the debug i get past -[IntroductionView viewDidLoad]
to...
__forwarding_prep_0___
___forwarding___
-[NSObject doesNotRecognizeSelector:]
objc_exception_throw
___TERMINATING_DUE_TO_UNCAUGHT_EXCEPTION___
 

robbieduncan

Moderator emeritus
Jul 24, 2002
25,611
893
Harrogate
dejo has thrown you a curve ball. imageWithContentsOfFile: is a class method, not an instance method so you should be calling it on UIImage as you were initially.
 

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
372
25
dejo has thrown you a curve ball. imageWithContentsOfFile: is a class method, not an instance method so you should be calling it on UIImage as you were initially.

ooh I'm so confused :confused: :D

So if I set it back to use imageNamed, how can i set image to nil or otherwise release the cache?
 

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
372
25
Yes. As suggested above load them through imageWithContentsOfFile: and retain/release them yourself as required.

Aha sorry didn't get you were just referring to the UIImage alloc...

And it looks as though the crash that was still happening when I included [image release]; was a result of my attempting to release NSString *imageName in viewDidUnload :D bit more testing just to make sure but it looks like it works! You guys truly are demi-gods. thank you :D
 

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
372
25
Sorry getting ahead of myself again... i'm still not releasing the images properly - any hints as to what and where?
 

robbieduncan

Moderator emeritus
Jul 24, 2002
25,611
893
Harrogate
Sorry getting ahead of myself again... i'm still not releasing the images properly - any hints as to what and where?

When you are done with them. Basically when you won't have a reference to it yourself you need to ensure that you've released anything created with alloc/init or copy (but not any other method types).
 

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
372
25
When you are done with them. Basically when you won't have a reference to it yourself you need to ensure that you've released anything created with alloc/init or copy (but not any other method types).

So what I have is
Code:
NSString *path = [[NSBundle mainBundle] pathForResource:[NSString 
                 stringWithFormat:@"image%d",i] ofType:@"png"]; 
        UIImage *image = [UIImage imageWithContentsOfFile:path]; 
        UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
if I put [image release]; here the view will not open a second time... can't put it in the dealloc because it's not been declared... imageView is released fine after being added to the scrollview subview..

Should I be declaring image somehow so that I can release it in the dealloc or in the viewDidUnload?
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.