I'm trying to figure out why pushing a viewController in my tableView using didSelectRowAtIndexPath would cause a crash in iOS 4.3, but in iOS 5.0+, it works fine.
It crashes right when I call:
self.customViewController = [[[CustomViewController alloc] initWithNibName:#"CustomViewController"bundle:nil] autorelease];
anytime after the first time the customViewController has been pushed.
Here's my relevant code:
#property (nonatomic, retain) CustomViewController *customViewController;
-(void) dealloc // Dealloc of tableView.
{
[customViewController release];
customViewController = nil;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.customViewController = [[[CustomViewController alloc] initWithNibName:#"CustomViewController"bundle:nil] autorelease]; // Release old, allocate new, set it.
[[self navigationController] pushViewController:customViewController animated:YES];
[customViewController release]; // Balance out pushViewController's retain.
}
Thanks.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.customViewController = [[[CustomViewController alloc] initWithNibName:#"CustomViewController"bundle:nil] autorelease];
[[self navigationController] pushViewController:customViewController animated:YES];
[customViewController release]; // Balance out pushViewController's retain. ---->NO
}
The last release is an extra one that is not needed.
You already have done an autorelease on it to have it's retain count down by one.
We will analyse this line
self.customViewController = [[[CustomViewController alloc] initWithNibName:#"CustomViewController"bundle:nil] autorelease];
you create a CustomViewController retain count == 1.
You say autorelease on it so retain count will be 0 later (probably the end of the run loop), but for now it's still 1 that is why you still have access to it, but treat it as 0.
After that you say self.customViewController, that property is retain, so retain count == 1.
And you are taking care of that 1 in your dealloc.
As of your comment :
// Balance out pushViewController's retain.
You don't Balance those, you balance only the one YOU own. If the system make retain on your objects, it will release them when the system don't need them anymore.
Don't release the customViewController. You've already autoreleased it when you assigned it, so you've already given up ownership from the alloc. You don't have to release or autorelease the object again. The navigation controller takes ownership and will relinquish it by itself at the appropriate time.
Also, it's coincidence that you may see it in one version and not another. This is a memory management problem, so any corruption you might see (crashes, etc.) will be dependent on the state of memory on the device whenever you run the app. You may see a crash all the time, never, or only after running Skype but before opening the Photos app.
A good way to look for these is to enable zombies during your debugging sessions. With zombies enabled, objects are never actually released. Instead they are put into a zombie state and if they're ever sent a message again, they will abort the app and show you where the stray message was sent to help you debug your memory issues.
Related
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
calling [myString release] does NOT decrement [myString retainCount]
I am in very big problem please help me to solve the same.
I am creating a view in custom way as
UIView *view = [self getCustomVIew:index];
[view release];
view = nil;
In getCustomVIew:index, I am allocating and returning the view. When I release view as
[view release];
it is not releasing the view from getCustomView:index and the reference count is not decremented.
Please help me to release the view returned from getCustomVIew:index and to maintain the memory size.
Instruments is just increasing the overall size of program when the views get deleted.
You don't need to release view from that getCustomVIew method. Returned view from should be already marked as autorelease
-(UIView*) customView{
UIView *v = [[UIView alloc] init];
...
return [v autorelease];
}
You need not to release that view in getCustomVIew:. Instead send autorelease signal before returning. After you get that view object also, you need not to release since it is already autoreleased.
your code seems fine and if you are trying to use "view.reatinCount" to ensure that view is released, it is not good.
here is the exact duplicate issue you may refer:
calling [myString release] does NOT decrement [myString retainCount]
I got a UIImageView that i set in the nib file. I download an image from internet and sets the image to the UIImageView. When i'm releasing it has retain count 2? If i'm using only 1 release it won't show any memory leak but i can see in "Instrument Allocations" that it never gets released. When i release the UIImageView twice like below then it works good. But i should never release it twice?!?!
in Header:
IBOutlet UIImageView *background;
in the .m loading the image:
/* Load Image code */
id path = [NSString stringWithFormat:#"http://www.image.com/aImage.jpg"];
NSURL *url = [NSURL URLWithString:path];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSData* urlData = [[NSData alloc] initWithContentsOfURL:url];
[background setImage:[UIImage imageWithData:urlData]];
[urlData release];
[pool release];
in dealloc function:
- (void)dealloc {
NSLog(#"Backgroud count: %i",[background retainCount]); // Prints 2
[background release];
[background release]; // Does not "leak" if i have 2x release
[super dealloc];
}
This is the only code that is useing the UIImageView Background.
EDIT:
something i forgot to mention is that i run this code inside a for loop like this. but this for loop will only execute once! But it shouldn't matter?
for (id theKey in dictionary) {
/* Load Image code above is here */
}
I believe that I've figured out what the trouble is. Apple recommends that you retain the objects you connect to through IBOutlets (your image view, in this case). You said that you haven't done so, but you should be following Apple's recommendation. The reason that you should is outlined in a iphonedevsdk.com forum post about this problem, which links to a Big Nerd Ranch blog post that lays it all out.
On iOS, the nib loading mechanism uses the setter if your outlet has one, but it uses key-value coding if not; specifically, it uses setValue:forKey:, which retains the value (this is documented, but somewhat unexpected). Your image view, being the subview of your view controller's top view, is retained by that view. It's also retained by this key-value setting procedure. So, unbeknownst to you, your objects have two references to the image view. Apple makes the retaining property suggestion so that it becomes knownst to you that the view is being retained.
You still shouldn't be worrying about the retain count as such, but you should do one of two things: make this IBOutlet a retained property and release it in both viewDidUnload and dealloc (just once each, though!), or follow BNR's suggestion and make the property explicitly assigned:
#property (assign, nonatomic) IBOutlet UIImageView *background;
in which case you do not have to release it yourself. In both cases, make sure you #synthesize the property accessors.
Previously:
Don't look at retain count, and if there's no leak being detected, then don't worry about it. The UIKit framework is likely retaining the view for reasons that you aren't privy to.
Additionally, if background isn't a retained property:
#property (retain) IBOutlet UIImageView *background;
and you're creating it in the xib, you shouldn't be releasing it at all, because you don't own it. That is, you aren't responsible for its memory; the actions that give you that responsibility are: calling retain on the object, or creating it using a method whose name begins with alloc, copy, mutableCopy, or new.
I don't know much about nib file, i used to follow like below
background=[[UIImageView alloc] initWithFrame:CGRectMake(0,0,320,480)];
//Now the reatin count is 1
[background setImage:[UIImage imageWithData:urlData]];
[someView addSubview:background];
//Now the ratainCount will be 2 since we added the imageview to the superview
[background release];
//i will release immediately so the retain count drops to 1
. . .
//in dealloc or viewDidDisaaper i will remove the imageview from its superview
//then the retainCount will become 0 and the instance will be deallocated.
//it works for we without any memory leakage
[background removeFromSuperview];
-(IBAction) btnLoginPressed{
Login *loginOverView = [[Login alloc] initWithNibName:#"Login" bundle:nil];
[self.navigationController pushViewController:loginOverView animated:YES];
[loginOverView release];
}
loginOverView will never get released?
Why do you think it will never get released?
You have done the right thing by balancing the init with a release.
(in the second line the navigationController does retain login but it will release it itself when it is necessary)
You have released the object which you have taken ownership of through alloc or new. So according to the Memory Management guidelines you must release it. So you have done the right thing.
Have only one question "How to right dealloc object when app goes to background ?". I'm working on some app, everything works great and fine. When i put app to background and then start it again it's crashes. Not immediately(app stars right where i closed it) , but when i choose some of cells or just scroll tableview for example. So i'm pretty sure that app calls already released data.
here is example of dealloc
- (void)dealloc {
[anObject release];
[array release];
[locationManager release];
[currentLatitude release];
[currentLongitude release];
[filteredListContent release];
[super dealloc];
}
Any advises?
Unless you specifically deallocate things in your app delegate's applicationDidEnterBackground method, nothing should be deallocated for you automatically.
I have never experienced the problem you're having. Are you certain the same thing doesn't happen after a while even if you never put it in the background?
Here's a good explanation of how all of the backgrounding stuff fits together:
Understanding iOS 4 Backgrounding and Delegate Messaging
I review and re-write my own code. The problem was as i expected in deallocation of memory. I used autorelease where i shouldn't. It still weird cause app did crash when i open it again.
Thanks everyone, not you guys who gift to me 2 negs, you are jerks. Don't be offended,but instead of give some advise you just give ungrounded negs.
Never release any object like this. Use
- (void)dealloc {
if(anObject){
[anObject release];
anObject = nil;
}
if(array){
[array release];
array = nil;
}
//same approach for all.
[super dealloc];
}
Maybe someone can help me with this strange thing:
If a user clicks on a button, a new UITableView is pushed to the navigation controller. This new view is doing some database querying which takes some time. Therefore I wanted to do the loading in background.
What works WITHOUT leaking memory (but freezes the screen until everything is done):
WorkController *tmp=[[WorkController alloc] initWithStyle:UITableViewStyleGrouped];
self.workController=tmp;
[tmp release];
[self.workController loadList]; // Does the DB Query
[self.workController pushViewController:self.workController animated:YES];
Now I tried to do this:
// Show Wait indicator
....
WorkController *tmp=[[WorkController alloc] initWithStyle:UITableViewStyleGrouped];
self.workController=tmp;
[tmp release];
[self performSelectorInBackground:#selector(getController) withObject:nil];
}
-(void) getController {
[self.workController loadList]; // Does the DB Query
[self.navigationController pushViewController:self.workController animated:YES];
}
This also works but is leaking memory and I don't know why !
Can you help ?
By the way - is it possible for an App to get into AppStore with a small memory leak ? Or will this be checked first of all ?
Thanks in advance !
No, small memory leaks will not (most likely) you application to be rejected from appstore.
In your example as you run your method in separate thread you should create and dispose NSAutoreleasePool object for that thread to handle autoreleased objects. Following changes to getController method should do the trick:
-(void) getController {
NSAutoreleasedPool *pool = [[NSAutoreleasedPool alloc] init];
[self.workController loadList]; // Does the DB Query
[self.navigationController pushViewController:self.workController animated:YES];
[pool release];
}
For more details see Autorelease Pools section in memory management guide. Relevant quote from there:
If you spawn a secondary thread, you
must create your own autorelease pool
as soon as the thread begins
executing; otherwise, you will leak
objects. (See “Autorelease Pools and
Threads” for details.)
Btw, you're calling pushViewController: from a background thread. This is bad.
You should only do things to the UI - like pushing view controllers and changing UI items - from the main thread. If you don't, things break.
See the Cocoa Fundamentals Guide section titled "Are the Cocoa Frameworks Thread Safe?": it says "All UIKit objects should be used on the main thread only."