Okay, so I have done a ton of research on this and have been pulling my hair out for days trying to figure out why the following code leaks:
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
UIImage *comicImage = [self getCachedImage:[NSString stringWithFormat:#"%#%#%#",#"http://url/",comicNumber,#".png"]];
self.imageView = [[[UIImageView alloc] initWithImage:comicImage] autorelease];
[self.scrollView addSubview:self.imageView];
self.scrollView.contentSize = self.imageView.frame.size;
self.imageWidth = [NSString stringWithFormat:#"%f",imageView.frame.size.width];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
Both self.imageView and self.scrollView are #propety (nonatomic, retain) and released in my dealloc.. imageView isn't used anywhere else in the code. This code is also run in a thread off of the main thread. If I run this code on my device, it will quickly run out of memory if I continually load this view. However, I've found if I comment out the following line:
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
UIImage *comicImage = [self getCachedImage:[NSString stringWithFormat:#"%#%#%#",#"http://url/",comicNumber,#".png"]];
self.imageView = [[[UIImageView alloc] initWithImage:comicImage] autorelease];
//[self.scrollView addSubview:self.imageView];
self.scrollView.contentSize = self.imageView.frame.size;
self.imageWidth = [NSString stringWithFormat:#"%f",imageView.frame.size.width];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
Memory usage becomes stable, no matter how many times I load the view. I have gone over everything I can think to see why this is leaking, but as far as I can tell I have all my releases straight. Can anyone see what I am missing?
You autorelease your imageview upon init, and then retain it by assigning it to self.imageView, then adding it as a subview retains it again. So, when the pool is drained, it gets a release message. When it is removed as a subview it gets a release message. Then if you dealloc, it gets a third release message. One of those three is not occurring. You say it's released in dealloc, so that's not it. The autorelease pool can be trusted to drain at some point, so that's not it. I would either make sure to remove it as a subview at some point, or get rid of one of your retain calls.
And.. shouldn't this:
self.imageView = [[[UIImageView initWithImage:comicImage] autorelease];
be this?:
self.imageView = [[UIImageView alloc] initWithImage:comicImage];
When calling this line:
[self.scrollView addSubview:self.imageView];
self.imageView is retained by its super view and when you don't need imageView anymore you should call:
[self.imageView removeFromSuperview];
This will call release on self.imageView.
p.s. You can track your ref counts by calling
NSLog(#"RefCount: %d", [self.imageView retainCount]);
add this line above
self.imageView = [[[UIImageView initWithImage:comicImage] autorelease];
to track the refCount. (Better option is to use Instruments but you already know that :))
Edit: It is a good practice to [[alloc] init] objects when you have retain properties like this:
UIView *myView = [[UIView alloc] init];
self.myCustomView = myView;
[myView release];
Otherwise you'll get self.myCustomView retained twice.
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
Fine.
UIImage *comicImage = [self getCachedImage:[NSString stringWithFormat:#"%#%#%#",#"http://url/",comicNumber,#".png"]];
Don't call methods get* anything unless you are following standard Cocoa patterns (which this method is not). Just call it cachedImage:.
self.imageView = [[[UIImageView initWithImage:comicImage] autorelease];
You are missing an alloc call; that should be:
self.imageView = [[[UIImageView alloc] initWithImage:comicImage] autorelease];
Or (if you want to avoid the autorelease pool; probably not an issue here):
UIImageView *iV = [[UIImageView alloc] initWithImage:comicImage];
self.imageView = iV;
[iV release];
[self.scrollView addSubview:self.imageView];
self.scrollView.contentSize = self.imageView.frame.size;
self.imageWidth = [NSString stringWithFormat:#"%f",imageView.frame.size.width];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
All fine. If there is a leak, it is either because imageView isn't released in dealloc or something else is hanging on to it (scrollView not being released, perchance?). Instruments can do a wonderful job of tracking down leaks, etc....
What the "leaks" tool looks for is objects that no longer have any references to them. In this case, it is quite likely that you have references remaining.
Frankly, given that you are easily able to reproduce the growth through repetition, Heapshot analysis will quite likely be highly applicable.
I wrote up a guide on Heapshot analysis a bit ago.
Related
I am new to iPhone development.
I am developing an application in which I am using Single tone class.
When I am creating an object of single tone class it is giving me memory leak on analyzing my code. It is giving message as "Potential leak of an object" and "Allocated object is not referenced later". But I am using that object in my code.
following is my code where I have created single tone class object
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Inside View");
self.navigationController.navigationBar.topItem.title = #"Menu List";
UIImage *image = [UIImage imageNamed:#"Navigation_bar.png"];
[_bgImage setFrame:CGRectMake(0,-45,320,510)];
[self.navigationController.navigationBar setBackgroundImage:image
forBarMetrics:UIBarMetricsDefault];
[self.tabBarController.tabBar setBackgroundImage:[UIImage imageNamed:#"Tab_bar.png"]];
[self.navigationItem setHidesBackButton:YES];
menuTableView.backgroundColor=[UIColor clearColor];
menuTableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
_hotelMenu=[SharedHotelMenu sharedInstanceMethod];
_queryFormatter=[[DatabaseQueryFormatter alloc]init];
_isSearchOn=NO;
_searchResult=[[NSMutableArray alloc]init];
_categorySearch.layer.cornerRadius = 19;
_categorySearch.clipsToBounds = YES;
_categorySearch.delegate=self;
UIView *_paddingView=[[UIView alloc] initWithFrame:CGRectMake(0,0,5,10)];
_categorySearch.leftView=_paddingView;
_categorySearch.leftViewMode=UITextFieldViewModeAlways;
[_paddingView release];
UIView *_paddingRightView=[[UIView alloc] initWithFrame:CGRectMake(0,0,30,10)];
_categorySearch.rightView=_paddingRightView;
_categorySearch.rightViewMode=UITextFieldViewModeAlways;
[_paddingRightView release];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(searchBar)
name:UITextFieldTextDidChangeNotification object:_categorySearch];
}
}
I have created single tone class object as _hotelMenu=[SharedHotelMenu sharedInstanceMethod];
As far as I can see in your code is that this line may cause memory leak
menuTableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
As this is #property(nonatomic, retain) UIView *tableFooterView and it will retain your object and thus retain count becomes 2.
Use this
UIView *footerView = [[UIView alloc] initWithFrame:CGRectZero];
menuTableView.tableFooterView = footerView;
[footerView release];
I just discovered the Build > Analyze feature of XCode today so I am trying to go through and address all the errors it is finding. There are a few lines XCode finds exception with that are confusing me:
//Test View
self.imageViewTest = [[UIImageView alloc] init];
self.imageViewTest.frame = CGRectMake(0, 0, 100, 100); // <=== Leak
[self.view addSubview:self.imageViewTest];
//Test View 2
self.imageViewTestB = [[UIImageView alloc] init];
self.imageViewTestB.frame = CGRectMake(0, 100, 100, 100); // <=== Leak
[self.view addSubview:self.imageViewTestB];
and later in my setup of video capture
self.captureOutput = [[AVCaptureVideoDataOutput alloc] init];
captureOutput.alwaysDiscardsLateVideoFrames = YES; // <=== Leak
The warning at each of these lines is "Potential leak of an object". All 3 of these objects are sent the release message in my dealloc method. What could be wrong here?
Thanks!
If you are not using ARC and your properties are setup with the retain attribute, then yes, these are leaks. This line:
self.imageViewTest = [[UIImageView alloc] init];
should be:
UIImageView *iv = [[UIImageView alloc] init];
self.imageViewTest = iv;
[iv release];
or:
self.imageViewTest = [[[UIImageView alloc] init] autorelease];
Or better yet, use ARC. It makes things SO much easier.
This is my first post and you are my last hope.
I have a list with images in my iPad app, if you select one, my class MyViewController which extends UIViewController is loaded and shown (by just setting window.rootViewController, by using UINavigationController, by presentModalViewController - tried everything, doesn't make any difference). It has a UIScrollView inside, which adds a big version of the image to the UIScrollView with this code:
UIImageView *tempImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[[delegate getImageNames] objectAtIndex:(gameNr*2)]]];
tempImageView.frame = CGRectMake(0,0,tempImageView.frame.size.width/2,tempImageView.frame.size.height/2);
[self setMyImage:tempImageView];
[tempImageView release];
myScrollView.contentSize = CGSizeMake(myImage.frame.size.width, myImage.frame.size.height);
myScrollView.maximumZoomScale = 2.0;
myScrollView.minimumZoomScale = 1.0;
myScrollView.clipsToBounds = YES;
myScrollView.bounces = NO;
myScrollView.delegate = self;
[myScrollView addSubview: myImage];
myImage is a (nonatomic, retain) property of the type UIImageView inside my MyViewController.
After pressing a button and go back to the list of images, I release the reference to MyViewController and it's dealloc method is called like this:
- (void)dealloc
{
CFShow(#"dealloc!");
for(UIImageView *subview in [myScrollView subviews]) {
if(subview.frame.size.width > 20){
[subview removeFromSuperview];
[subview setImage:nil];
}
}
[myImage setImage:nil];
self.myScrollView = nil;
self.myImage = nil;
[myScrollView release];
[myImage release];
[super dealloc];
}
It works so far, but the problem is, that the memory of the images is not really deallocated this way. After opening and closing about 12 images, the app crashes with memory warning.
This is also confirmed by the report_memory method I got from here:
iOS Low Memory Crash, but very low memory usage
The dealloc method IS called, that's triple checked.
I checked the variables myImage and myScrollViewvia breakpoint in the debugger, they are set to 0x0 by those commands in the dealloc method. Why is the memory not freed????????
Thanks for any suggestion, I am working on this since three whole days, it's driving me crazy.
Replace this:
self.myScrollView = nil;
self.myImage = nil;
[myScrollView release];
[myImage release];
with this:
[myScrollView release];
self.myScrollView = nil;
self.myImage = nil;
And post code where you define property myScrollView and init it.
And you don't need loop for(UIImageView *subview in [myScrollView subviews]). When you will release myScrollView object it will automatically release all its subviews.
I have a viewcontroller that repeatedly repositions 6 items within a uiscrollview. However, even though I've limited the number of items within the uiscrollview to 6, I'm still leaking memory when i update their position and their image. Can someone let me know if the following code which represents a unit within the uiscrollview is properly coded? startLoad is the method that I call after to reload the image.
#import "ScrollUnit.h"
#implementation ScrollUnit
#synthesize index;
#synthesize ProductToDisplay;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code.
}
return self;
}
-(void)startLoad
{
[imageview removeFromSuperview];
[imageview release];
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]
initWithTarget:self
selector:#selector(loadImage)
object:nil];
[queue addOperation:operation];
[operation release];
}
-(void)loadImage
{
NSString *myimageName = [self.ProductToDisplay valueForKey:IMAGEKEY];
NSString *myimageUrl = [NSString stringWithFormat:#"%#/%#",IMAGE_SERVICE,myimageName];
NSData* imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:myimageUrl]];
UIImage* image = [[[UIImage alloc] initWithData:imageData] autorelease];
[imageData release];
[self performSelectorOnMainThread:#selector(displayImage:) withObject:image waitUntilDone:NO];
}
- (void)displayImage:(UIImage *)image
{
imageview = [[[UIImageView alloc] initWithFrame:CGRectMake(9, 0, 320, 320)]retain];
imageview.contentMode = UIViewContentModeScaleAspectFit;
imageview.backgroundColor = [UIColor whiteColor];
imageview.image = image;
[self addSubview:imageview];
[imageview release];
//[image release];
}
- (void)dealloc {
[super dealloc];
}
#end
Get rid of the retain message on this line and you should be all set:
imageview = [[[UIImageView alloc] initWithFrame:CGRectMake(9, 0, 320, 320)]retain];
The reason why you need to do this is because you already own the object by calling alloc, so at that point, the relative reference count is 2.
Once you invoke addSubview:, passing in the imageview, the reference count gets bumped to 3, then right back down to 2 once you release it on the next line.
So once that object gets sent release in -dealloc, you're still stuck because the reference count is now 1, not 0 as you expected.
There is also another little thing that might bug (or not). You don't release imageview before you assign it in displayImage method. As long as only startLoad is called you are fine but if displayImage is called from the outside then you still leak.
You might want to use a property with retain and then synthesis the getter and setter methods. This way the iOS will release your previous assignment before it retains your new assigned object. That said then you need to release created image view right were you create it and you have to use "self.imageview" in order to make sure that you use the setter (setImageview).
I have an app where I create many uiviews and add them to the self.view of the UIViewController. My app is running really slowly. I am releasing all of my objects and have no memory leaks (I ran the performance tool). Can anyone tell me what could be making my app so slow? (code is below)
[EDIT] The array has around 30 items. [/EndEdit]
Thanks so much!
Here is the code for the loadView method of my UIViewController:
- (void)loadView {
UIView *contentView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
contentView.backgroundColor = [UIColor whiteColor];
self.view = contentView;
[contentView release];
int length = 0;
for(NSString *item in arrayTips)
{
length++;
[item release];
}
int index = 0;
for(NSString *item in arrayTitles)
{
SingleFlipView *backView = [[SingleFlipView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
backView.userInteractionEnabled = YES;
backView.backgroundColor = [UIColor whiteColor];
[backView setViewIndex:index];
[backView setLastViewIndex:length];
CGRect labelFrame = CGRectMake(10.0f, 0.0f, 300.0f, 30.0f);
UILabel *backLabel = [[UILabel alloc] initWithFrame:labelFrame];
backLabel.textAlignment = UITextAlignmentCenter;
backLabel.userInteractionEnabled = YES;
backLabel.text = item;
backLabel.font = [UIFont fontWithName:#"Georgia" size:24.0f];
backLabel.textColor = [UIColor blackColor];
backLabel.backgroundColor = [UIColor whiteColor];
CGRect textFrame = CGRectMake(10.0f, 30.0f, 300.0f, 110.0f);
UITextView *tbxView = [[UITextView alloc] initWithFrame:textFrame];
tbxView.textAlignment = UITextAlignmentCenter;
tbxView.userInteractionEnabled = YES;
tbxView.editable = FALSE;
tbxView.text = [arrayTips objectAtIndex:index];
tbxView.font = [UIFont fontWithName:#"Arial" size:14.0f];
tbxView.textColor = [UIColor blackColor];
tbxView.backgroundColor = [UIColor whiteColor];
//CGRect labelFrame = CGRectMake(10.0f, 0.0f, 84.0f, 30.0f);
UIImage *nextTip = [[UIImage imageNamed:#"NextTip.png"] retain];
UIImageView *nextTipView = [ [ UIImageView alloc ] initWithImage:nextTip];
nextTipView.frame = CGRectMake(230.0f, -10.0f, 84.0f, 30.0f);
nextTipView.userInteractionEnabled = YES;
UIImageView *view = [[ UIImageView alloc ] init];
view.userInteractionEnabled = YES;
if(self.sexString == #"Men")
{
UIImage *imgTip = [[UIImage imageNamed:#"feet_small.jpg"] retain];
view.image = imgTip;
view.frame = CGRectMake(0.0f, 110.0f, 416.0f, 228.0f); //59*161
[imgTip release];
}
[backView addSubview:view];
[backView addSubview:tbxView];
[backView addSubview:backLabel];
//[backView addSubview:nextTipView];
[self.view addSubview:backView];
[backView release];
[backLabel release];
[nextTip release];
[nextTipView release];
[tbxView release];
[view release];
index++;
[item release];
}
}
It's going to depend upon how many items are in arrayTitles. If you're just adding one or two of these, you shouldn't see a HUGE slowdown; more, and you will. You should probably take a look at the way UITableView handles its cells; only create these as they're actually needed/used, or, better yet, only create one of these, and set its contents on-the-fly.
A few other notes:
== is not a valid string comparison operator in Objective-C; use [string1 isEqualTo: string2]
It appears you're trying to place a lot of these on screen at the same time, which doesn't seem like it would make a lot of sense.
it looks like you've got a spurious [item release] at the end there (you're never retaining item, so there's no need to release it.
the whole first loop ( for(NSString *item in arrayTips)... frightens and confuses me; items in NSArrays are already retained by the array. You shouldn't have to explicitly retain/release them in this way.
Having deep view hierarchies can lead to slow downs that you can often fix through flattening them some with custom views, but if you are using simple views you can have dozens on the screen with no perceptible performance impact, so in general I recommend ignoring how many views you have when you are developing, and then reducing the view count if it proves to be a performance problem.
Having said that, you appear to be setting up something with an unboundedily large number of views which is not good. Without knowing how many entries there are in array titles I can't tell you what is going on exactly, but I suspect that while the actual visual heiarchy with each backView you are creating is fine, making a backView for each item in the array and using indices to have the front most one hide all the other ones behind it is causing you to have way too many views.
So, how to test it:
Add a break to the bottom of your for loop. Make the loop drop out after a single iteration and see if performance improves. If it does, then the huge view hierarchies are your issue. YOu may have to hack up the routine that changes the indexes to make sure it never swaps to an invalid index to test.
If that is the case you have a few options. You could implement a custom view and flatten every backview into a single view, but depending on how many you have that mat not be sufficient, and it is more work than simply building the back views the way you currently are, but on demand instead of at load time:
1) Refactor the code in your for loop into a separate method that makes a backView for a specific title and attaches it to the view at that time.
2) Where ever you are currently altering the indexes to make the backview visible, instead call the new method to actually build and attach the backview at that time
Don't forget to make as many of your views opaque as you can. Transparent views are a major source of performance issues.