What is the difference between
difference between [self.view release] and [view release].
I get a memory leak error in [self.view release]
[view release] is calling NSObject's - (void)release method directly on an instance variable named view.
[self.view release] is calling - (UIView *)view (I'm assuming view is of type UIView) on self and then calling - (void)release on the returned object.
There shouldn't be any practical difference. However, since one reason for using properties is to eliminate the need for explicit memory management, [self.view release] would be a strange thing to do. If it's a property, let the property accessors retain and release the object as necessary.
It's hard to see why you'd get a real leak from [self.view release]. Leaks come from failing to properly release an object -- I don't think you can create a leak by releasing something. It may be that accessing the object through a property prevents the static analyzer from connecting the dots from +alloc to -release.
Related
There are cases where you want to present an alert style view controller using your own animations (instead of using presentModalViewController:animated: or UIAlertView).
The right way of releasing this view controller would be in a callback called when the view disappears, but setting up a delegate and all that seems overkill.
So I do this:
- (void)dismiss
{
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
self.view.alpha = 0;
} completion:^(BOOL finished) {
[self.view removeFromSuperview];
[self autorelease];
}];
}
and the presenting object would not release or autorelease the view controller. Memory management wise I see no issue with this. Is it bad practice?
I would consider this bad practice.
Only objects that have called retain on this object should call release or autorelease on it.
I do assume that you haven't called [self retain]
I'm not even sure this would work as you expect it to. Can you guarantee that self would need releasing at that point and why couldn't an object that has retained it called release itself?. Are you trying to force self to dealloc?
If self was deallocated at this point, any other object that was expecting self to still be alive would be passing messages to nil, or worse, the memory might be re-allocated and those objects would be sending messages to arbitrary objects.
It seems like bad practice however don't see any effect on memory. In terms of modal view why don't you just pop it back if thats what you want to achieve using:
[self.navigationController popViewControllerAnimated:YES];
When I use a UIPopoverController and give it a contentViewController, I cannot seem to get the contentViewController to correctly be deallocated (as evidenced by the fact that the contentViewController.viewDidUnload is never getting called).
Code to create and display the popup:
PopupTestViewController *popupTest = [[PopupTestViewController alloc] initWithNibName:#"PopupTestViewController" bundle:nil];
popupTest.mainViewController = self;
self.popoverController = [[UIPopoverController alloc] initWithContentViewController:popupTest];
self.popoverController.popoverContentSize = popupTest.view.frame.size;
self.popoverController.delegate = self;
[self.popoverController presentPopoverFromRect:button.frame inView:button.superview permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
Now I am assuming that the contentViewController (in the code above, this is PopoverTestViewController) should be deallocated when the UIPopoverController is closed (whether by clicking off of it, or be explicitly dismissing it). But viewDidUnload is never called. I did notice, however, that if I define a dealloc method for PopoverTestViewController, that is called appropriately.
So my question is: why is viewDidUnload never getting called?
(And I'm using ARC).
viewDidUnload is not guaranteed to get called. It only gets called when the application receives a memory warning and the receiving ViewController has view loaded but is off screen.
When the view is loaded and retain count reaches zero, viewDidUnload is not called.
You can find more details in the documentation.
Instructions on when to release what objects can be found in the same document.
This is a little different than I've used UIPopoverController. Since you manually alloc'ed popupTest, you definitely need to manually release it. The UIPopoverController instance will retain popupTest when initWithContentViewController: is called.
Furthermore, if you are defining the popoverController property with retain, then you're getting a double-retain when you use the self.popoverController setter by assigning directly from the alloc. The general pattern for setting a #property is:
#property (nonatomic, retain) Foo* foo;
...
Foo* aFoo = [[Foo alloc] init];
self.foo = aFoo;
[aFoo release];
I have a UIViewController that is popped from the navigation stack on iPhone and removed by setRootViewController on the iPad.
In both cases the UIViewController fails to dealloc, which means that something is hanging on to it; this is confirmed by logging [self retainCount] which is two, right before the pop or the setRootViewController (for some reason it's four in ViewWillDisappear).
The UIView has audio (using Flite Text to Speech - which uses AVFoundation.framework) and animation.
What things should I look for that could increasing my retain count in the view controller and stopping the view controller from being politely dealloced as it should be.
Here's how the view is pushed onto the view stack or set as the RootViewController;
-(IBAction)pushShare:(id)sender{
ShareViewController *shareViewController = [[ShareViewController alloc] initWithNibName:#"ShareViewController" bundle:nil];
NSLog(#"1. SVC Retain count %d", [shareViewController retainCount]);
[shareViewController setParentIsIpadMake:YES];
NSLog(#"2. SVC Retain count %d", [shareViewController retainCount]);
[shareViewController setStory:story];
NSLog(#"3. SVC Retain count %d", [shareViewController retainCount]);
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
StoryBotAppDelegate *appDelegate = (StoryBotAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.window setRootViewController:shareViewController];
NSLog(#"4. SVC Retain count %d", [shareViewController retainCount]);
} else{
[self.navigationController pushViewController:shareViewController animated:YES];
NSLog(#"4. SVC Retain count %d", [shareViewController retainCount]);
}
NSLog(#"releasing Svc...");
[shareViewController release];
NSLog(#"5. SVC Retain count %d", [shareViewController retainCount]);
}
First, try running Xcode's static analyser, it may find what is wrong.
You can also try overriding retain method with calling [super retain] and logging. Moreover you can put a breakpoint there and look at the stack when Xcode stops on it.
Or you can run ARC conversion tool and see what happens.
Retain count method is next to useless. The system libraries will be retaining your VC and releasing it. Which is why sometimes it will be a strangely high number. There are lots of topics on this.
Don't be concerned about the view animation delegate having the VC. The view and the VC are intimately tied together and released together, and the view doesn't retain its animation delegate - it just assigns it as a reference.
If u are testing in the simulator, force a memory warning and see if it is dealloc'ed. Dealloc'ed isn't always called immediately when you pop the VC.
Run instruments leaks. Does it say the VC is leaking memory? Run instruments with Zombies to find out whe it is leaking.
I read this question about NSTimer stopping the dealloc of a UIView. Turns out I had a NSTimer firing my animations which was stopping dealloc from happening nicely. I fixed it by adding a method called killTimer:
-(void)killTimer{
[animationTimer invalidate];
animationTimer = nil;
}
which I called on closing the view.
Thanks heaps for the answers to this question though - they were super helpful. I didn't actually know about the static analyzer until asking - or about how useless retain counts are; so +1 to you both.
Resloved!
Thanks to Lone Gunman, this issue was due to an oversight of not setting the many delegates to nil before releasing them.
This is a strange one... I'm familiar with basic memory management but I think something is unusual about what I am seeing. Here is a little background...
I have a NavigationController that handles the navigation between the following ViewControllers:
Home -> Games -> Game
When running the code it falls down when leaving the Game. Within the GameViewController there is a dealloc method that resembles:
- (void)dealloc
{
[board release];
[opponentsViewController release];
[instructionsViewController release];
[imgPicker release];
[gameView release];
[super dealloc];
}
When the navigation controller goes back to the Games list (from the Game) it throws a EXC_BAD_ACCESS. So I bring up my trusty profiler and check for Zombies. Alas, just as I expected a message is being sent to a deallocated object! Digging deeper I find there to be 3 entries in the object's history:
Board getting alloc'd (called by Game's init method)
Board getting released (called by Game's dealloc method)
Board being Zombie'd (called by Game's dealloc method)
Both calls 2 and 3 are called from UINavigationController setDisappearingViewController.
In my dealloc method I set breakpoints to each release call, when doing so - the [board release] call occurs, then the [opponentsViewController release] call occurs then the [board release] call occurs again. So I'm seeing the dealloc method does not finish completely and calls again.
What might be causing this?
Edit: This is the GameViewController Implementation
Code from the Games controller that adds this game:
-(void) gotoGame:(int)tag {
game = [[GameViewController alloc] init];
[self.navigationController pushViewController:game animated:YES];
[game release];
}
Edit: This is the GameViewController Header
I would try setting all your ivar's delegates to nil (EDIT: in dealloc). I've had a similar problem with a fetched results controller. Failed to set the its delegate to nil in dealloc and the core data stack still had a pointer to it when the view controller was released.
So that's my bet, set ivar delegates to nil in dealloc, although I can't see your header to know what protocols your are conforming to be sure.
EDIT: Explanation
Setting a delegate is actually giving the object that is doing the delegation a pointer (I believe it usually an assigned property).
#property (assign) delegate;
I'll use the problem I had as an example.
So let's say you have a view controller that has a fetchedResultsController as an ivar. When you set the FRCs delegate:
fetchedResultsController.delegate = self;
and the view controller gets released, any object that is using that pointer still thinks it's live. You would think since the FRC is getting released in dealloc as well, you'd be fine(which is why it took me 4 days to figure this out :) ), but sometimes other parts of an implementation use your delegate as well. So the fix is:
-(void)dealloc
{
self.fetchedResultsController.delegate = nil;
[_fetchedResultsController release];
[super dealloc];
}
Note: as soon as the new tools are available to everyone you won't have to worry about this stuff anymore ^ ^;
try
- (void) dealloc {
if(game != nil){
//release here
[game release];
}
[super dealloc];
}
By the way it seems you have declare game in header file and just after pushing you are releasing it and also in dealloc method you are releasing it. Either remove the release call from dealloc method or change you method like this.
-(void) gotoGame:(int)tag {
GameViewController *game = [[GameViewController alloc] init];
[self.navigationController pushViewController:game animated:YES];
[game release];
}
UPDATE
Also you are not using the tag anywhere. Why don't you create your init method like this
GameViewController *game = [[GameViewController alloc] initWithTag:tag];
[self.navigationController pushViewController:game animated:YES];
[game release];
How is method removeFromSuperView: really works?
I got a problem of memory bad access when I want to reinit the view
- (id)init {
if (!(self = [super init]))
return nil;
_mainView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
NSLog(#"retainCount :%d", [_mainView retainCount]);
UIButton *reInitButton = [[UIButton alloc] initWithFrame:CGRectMake(0.0f,0.0f,90.0f,35.0f)];
[reInitButton addTarget:self action:#selector(buttonDidTapped:) forControlEvents:UIControlEventTouchUpInside];
[[self view] addSubView:_mainView];
NSLog(#"retainCount :%d", [_mainView retainCount]);
[_mainView release];
NSLog(#"retainCount :%d", [_mainView retainCount]);
return self;
}
- (void)buttonDidTapped:(id)sender {
[_mainView removeFromSuperView]; //crash during second times press the button
NSLog(#"retainCount :%d", [_mainView retainCount]);
_mainView = [[UIView alloc] initWithFrame[[UIScreen mainScreen] bounds]];
[[self view] addSubView:_mainView];
NSLog(#"retainCount :%d", [_mainView retainCount]);
[_mainView release];
NSLog(#"retainCount :%d", [_mainView retainCount]);
}
I have NSLog every times there are any retain or alloc or release keyword. And the result is very weird.
//init
retainCount : 1
retainCount : 2
retainCount : 1
//1st time pressed button
retainCount : 1 //remove super view didn't decrease
retainCount : 2
retainCount : 1
//2nd time pressed button
retainCount : 0 //crash. Memory bad access
The weird thing is why it didn't crash on 1st time pressed??
I think your problem is here:
[_mainView release];
You've dropped your reference to _mainView, and yet, by my reading, that's a member variable that you'll keep around and continue to invoke methods on. That's not valid. Once you've called -release, you've essentially told the system you're not going to use that object again, and you can't do anything useful with a stale pointer to that object, like you do when you call -removeFromSuperView on it later.
If you want to continue to keep _mainView around and call code on it, you need to keep a reference. Perhaps you should move the release to your object's -dealloc method. Alternatively you could -release it in the button method and re-create a new view the next time you need to.
As a helpful tip, a lot of programmers like to reset objects to NULL (or nil in objC-speak) after releasing them, as a reminder that you can't use that object again. If you -release something, you'd better mean it.
Lastly, I suggest you Google the term "reference counting" and read up on it; it's a more generic idiom than the specifics of NSObject, and it is likely to be useful to think of the basics and how you might implement this in another language, like say, C. This will help you reason better about reference counted objects.
NEVER USE RETAINCOUNT. Sorry for putting that in caps, but I can't figure out for the life of me why people still use it. It's a faulty reference for memory management. Use instruments or similar instead.
You shouldn't be accessing _mainView at that point. This may be hard to explain, so bear with me. We're going to count, but not absolute retain count, just your code's claims on the object.
You allocate memory for an object and point at it with _mainView:
_mainView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
You have 1 claim of ownership to that object. When you add it as the subview of another view, that view likewise makes a claim of ownership, but that's not yours, it's the view's. The fact that it makes the object in _mainView stick around is an accident, and you shouldn't rely on it. Then you release the object:
[_mainView release];
You have relinquished your ownership claim -- you now have 0 claims, and you should no longer try to access this object. You don't own it. Again, the fact that it still exists because another view is using it, and the fact that you still have a pointer to it, are accidents*, and you should not rely on them.
When it comes time to handle your button press, then, you are accessing an object over which you have no ownership:
[_mainView removeFromSuperView];
and this causes a crash, which may not be expected, but it is not unreasonable. By letting your claims of ownership go to 0, you told the system "I don't need this object anymore. I'm not going to access it after this point. If it disappears, I will not be affected." In fact, though, you do need it to stay around, and you do need to access it.
What you should do, then, is move the line:
[_mainView release];
to inside the button action, right after the call to removeFromSuperview.
*The second of which could be avoided by setting _mainView = nil; after you release it, in this case, but that won't solve the greater problem.