iOS Scrolling/Image memory leak...

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
359
16
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];
}
 

kainjow

Moderator emeritus
Jun 15, 2000
7,958
5
That code looks fine. How are you determining there's a leak?
 
Comment

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
359
16
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...
 
Comment

robbieduncan

Moderator emeritus
Jul 24, 2002
24,862
213
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.
 
Comment

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
359
16
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
 
Comment

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
359
16
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! :(
 
Comment

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
359
16
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...
 
Comment

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
359
16
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...
 
Comment

dejo

Moderator
Staff member
Sep 2, 2004
15,981
450
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:.
 
Comment

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
359
16
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
 
Comment

dejo

Moderator
Staff member
Sep 2, 2004
15,981
450
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.
 
Comment

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
359
16
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 :'(
 
Comment

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
359
16
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___
 
Comment

robbieduncan

Moderator emeritus
Jul 24, 2002
24,862
213
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.
 
Comment

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
359
16
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?
 
Comment

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
359
16
As far as I am aware you can't. You have no control over the internal caching.
There has to be some way to be able to use all these images throughout the different views without caching them all at once though right?!
 
Comment

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
359
16
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
 
Comment

MentalFabric

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

robbieduncan

Moderator emeritus
Jul 24, 2002
24,862
213
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).
 
Comment

MentalFabric

macrumors 6502
Original poster
Mar 10, 2004
359
16
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?
 
Comment

Similar threads

Register on MacRumors! This sidebar will go away, and you'll see fewer ads.