Quick question, hopefully I am just missing something simple. Ok I have one class that holds a pointer to another; MainMenuClass and NormalGameClass. Inside of the MainMenuClass I do the following.
m_NormalGame = [[NormalGameMode alloc] initWithNibName:#"NormalGameMode" bundle:[NSBundle mainBundle]];
m_NormalGame.delegate = self;
[self presentModalViewController:m_NormalGame animated:YES];
Now, I first noticed a problem whenever the NormalGameClass' dealloc function was not being called so I did some retainCount calls and for some reason once it makes its way back to the release function in MainMenu, its retain count is 6. Further digging has me very confused. The first line after viewDidLoad in NormalGameClass its [self retainCount] is 4. Anybody have any idea what could be going on here? I only call alloc on NormalGameClass once ever, and yet it is being retained up to 6? Strangely enough never past that. Thanks for any insight.
UPDATE: Was fiddling around with things and found this to be awkward.In the MainMenuClass, here is how I get rid of the NormalGame.
[self dismissModalViewControllerAnimated:NO];
m_NormalGame.delegate = nil;
[m_NormalGame release];
Now, with this setup, the dealloc for NormalGame is never called. However, if I call [m_NormalGame release] immediately after the one posted above, it calls the dealloc for NormalGame ...twice. =/ Paint me confused.
presentModalViewController retains the passed view controller, so you need to release the view controller passed if you do not autorelease it. In this case you would need to release m_NormalGame.
m_NormalGame = [[NormalGameMode alloc] initWithNibName:#"NormalGameMode" bundle:[NSBundle mainBundle]];
m_NormalGame.delegate = self;
[self presentModalViewController:m_NormalGame animated:YES];
**[m_NormalGame release];**
I would imagine that the -dismissModalViewControllerAnimated: call is not releasing the view controller until the dismissal is complete. You do need to balance your initial -alloc/-init of the controller with a -release, but you should not expect the -dealloc method to be called immediately. It may in fact be called during the next iteration of the run loop, if the object was autoreleased.
Are you saying that without two calls to release your dealloc is not called, or is it simply not called immediately?
Also, try not to inspect retain counts as that will only lead to confusion and headaches. Just follow the memory management rules properly.
Related
What's the equivalent of [[something retain] autorelease] in ARC?
I have a problem where a class DBRequest calls my delegate to signify completion. My delegate then sets the DBRequest instance to nil, which dealloc's it. But then when the stack pops out of my delegate and jumps back to the DBRequest, it of course then crashes.
If I wasn't in ARC, in my delegate I'd simply do [[theDbRequest retain] autorelease] before releasing my reference to it, so that it'd survive long enough until the next run loop autoreleased it.
What should I do in ARC?
How about adding something like
__strong DBRequest * myself = self;
[delegate reportDone];
I think that'll increment the self object until the end of the function preventing it from dying early.
My delegate then sets the DBRequest instance to nil, which dealloc's it. But then when the stack pops out of my delegate and jumps back to the DBRequest, it of course then crashes.
Surely this was always a bad strategy, and your [[theDbRequest retain] autorelease] was always just covering up the problem, yes?
Simply do nothing. So your instance variable sticks around; so what? You know that ARC will release it for you when you are dealloced.
The important thing is not to release theDbRequest, but to set theDbRequest's reference to you (the delegate) to nil, so it doesn't try to call you back when you no longer exist. Your own dealloc would be a good place to do that.
Hope I'm understanding the issue correctly. If not, post some code!
As #matt says if you simply do nothing ARC should clean up when your object is deallocated - assigning the DBRequest you create to an instance variable handles that (provided of course your object outlasts the object you are creating).
If you need to deallocate the DBRequest before your object dies then you need an ARC-compatible "trick" equivalent to [[theDbRequest retain] autorelease]. Now unless you are constructing your own auto release pools your previous approach would trigger at the end of the current event. Following that logic try:
Add a method to your class which simply sets theDbRequest to nil, let's call this cleanUpTheDbRequest.
Change your delegate callback to invoke [self performSelectorOnMainThread:#selector(cleanUpTheDbRequest) withObject:nil waitUntilDone:NO] instead of directly assigning nil to theDbRequest
This should delay the assigning of nil till after the end of the current event, just as your autorelease "trick" did. It also works if your DBRequest lives across multiple events - the previous method kicks in at the end of the event the autorelease is called in, this method at the end of the event the delegate method is called in.
I have created tableview in my view by programmatic like below
table = [[UITableView alloc] initWithFrame:CGRectMake(0, 44, 320, 370) style:UITableViewCellStyleDefault];
table.delegate = self;
table.dataSource = self;
table.separatorStyle = UITableViewCellSeparatorStyleNone;
[self.view addSubview:table];
in dealloc method i write like below
table.delegate = nil;
table.dataSource = nil;
[table release];
table=nil;
this the better way or below one is better
[table release];
table=nil;
I want to know if i dont reset delegate and dataSource what will happen
Thanq
If you are deallocating an object that acts as the delegate to other objects, you need to make sure that you have set their delegates to nil, before you call [super dealloc] (assuming the normal pattern that objects do not retain their delegates). This is because when [super dealloc] has returned, this object is no longer a valid object and the objects it is a delegate to effectively have dangling references, if they have not been set to nil.
In this particular case, you would probably get away without doing it because your object's dealloc probably won't get called except when the UI is being dismantled and the table view no longer needs to use its delegate or data source, but don't bet on it.
From Setting delegate to nil in dealloc:
It's a defensive programming move. It's clearing out the reference to the delegate object incase something else in your object tries to access the delegate after you've told it that you're done with it. As part of your dealloc you might have a method or do something that triggers a KVO notification that makes a call to the delegate. So setting the delegate's reference to nil prevents that from happening. If it did happen you could end up with some oddball crashes that are fun to reproduce and fix.
To add to the answers above, you do not need
table = nil;
in your dealloc. It won't hurt, but it is not necessary to nil out your ivars. Your view is being dealloc'ed and therefore your ivars will no longer be accessible. You are probably confusing that with:
self.table = nil;
which can function as an alternative way to release if you are accessing the ivar via a property.
Of course if you have ARC turned on, then you don't need the release at all.
And to answer your actual question, if you don't nil out the table's delegate and datasource on the dealloc of the view....nothing will happen. They are set to the view, which is in the process of being released. In this case, you will have no issues not doing it. In theory it's good form.
Why do I need to create a pointer just to allocate memory and then release it immediately?
In other words, can't I just do this:
self.viewController = [[HelloWorldViewController alloc] initWithNibName:#"HelloWorldViewController" bundle:[NSBundle mainBundle]];
instead of this:
HelloWorldViewController *newViewController = [[HelloWorldViewController alloc] initWithNibName:#"HelloWorldViewController" bundle:[NSBundle mainBundle]];
self.viewController = newViewController;
[newViewController release];
[EDIT]
To provide wider context on my question: (within #implementation of #interface HelloWorldAppDelegate : NSObject <UIApplicationDelegate>)
#synthesize window=_window;
#synthesize viewController=_viewController;
...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
HelloWorldViewController *newViewController = [[HelloWorldViewController alloc] initWithNibName:#"HelloWorldViewController" bundle:[NSBundle mainBundle]];
self.viewController = newViewController;
[newViewController release];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
...
- (void)dealloc
{
[_window release];
[_viewController release];
[super dealloc];
}
So, within the didFinish... method, can I use the short version I supplied way above, or would it leak?
[SECOND EDIT]
Seems I just can't give enough info. I'm always hesitant to post a huge pile of code.
I've got this in HelloWorldAppDelegate.h:
#property (nonatomic, retain) IBOutlet HelloWorldViewController *viewController;
So, correct me if I'm wrong. Given the declaration of viewController in the header file, if I use the shortcut method (in the first code snippet above), the program will leak. The object counter is incremented once by the alloc, and then a second time by the retain, then decremented once by the release, producing a net of +1 on the pointer reference counter.
You do have to release the object that you allocated, or you will end up with a memory leak. The assignment to self.viewController (presumably) retains the newly-alloc'd HelloWorldViewController, so you have two "claims" on the object -- one from your call to alloc and one from the retain, but you're not actually going to use it any more under the name newViewController, so you relinquish that particular claim by calling release.
The "long" form, using the temp variable, is in fact the correct way to do this. Sending release to the result of the property access: [self.viewController release]; right after it's set is quite likely to work, but is incorrect (and will generate a compiler warning for recent LLVM versions).
It's also possible to do this:
self.viewController = [[[HelloWorldViewController alloc] initWithNibName:#"HelloWorldViewController"
bundle:[NSBundle mainBundle]]
autorelease];
which says "I've created this thing to use it briefly, but I won't need it outside this immediate call stack, so go ahead and release it for me later."
If viewController isn't a retaining property, then all this is moot, and you should not be sending release to the new view controller, because it will then be deallocated before you can use it.
UPDATE: Your expanded question doesn't change anything*. If you don't send release before newViewController goes out of scope, you will end up with a leak.** The way this works is, any object (A, B, C) that needs to use object X, and therefore cares about keeping it around, sends retain to X. When you alloc an object, it's assumed that you need to use it. When any of A, B, or C no longer need X, it sends release to X, thereby saying "X can be deallocated and it won't affect me". You need to balance the number of claims you make (by using retain, alloc, copy/mutableCopy, or new) on an object with the number of relinquishments you make (by sending release or autorelease) or you will have either a leak or an accidental deallocation on your hands.
I'm going to give you a link to the docs now, but don't be annoyed, it's just one page that you need to read and internalize: The Fundamental Rule of Cocoa Memory Management.
*Actually, you left out the only part that could've changed the answer, namely your
#property () HelloWorldViewController * viewController;
declaration. If it says "retain" inside those parentheses, then when this property is set, your app delegate is sending retain to the passed object. If it says "assign", or the parentheses aren't there, then, like I said, you should not send release to that object.
**Note for any pedants looking on: you could of course send release twice to the viewController at some point, but that's worse than a leak.
Yes you can do this. Except you have to release self.viewController so that the retain count is 1 by the end of the snippet.
Let's count. Upon allocation, the release count is 1. Assiging it to self.viewController (assuming it's a property with retain behavior) increases it to two. Releasing it at the end of the second snippet makes it 1 again. The idea is that you'll release it completely in the dealloc of the current class, whatever that is.
The only wrinkle is that releasing an object variable assumes that you're done with it, and here you have to go, releasing self.viewController but using it later. Kinda smelly.
I'm getting an EXC_BAD_ACCESS after calling dismissModalViewControllerAnimated on my view controller. My project is based on the table view starter project, and RootViewController creates a view like this:
GobanVC *vc = [[GobanVC alloc] initWithNibName:#"GobanVC" bundle:[NSBundle mainBundle] coll:c];
[self.navigationController pushViewController:vc animated:YES];
[vc release];
In GobanVC.m, I'm handling a button to dismiss the view:
- (IBAction) onDone:(id) sender;
{
[self.navigationController popViewControllerAnimated:YES];
}
For some reason the GobanVC object is getting over-released. I ran the allocation instrument, and I can see the reference count get set to 1 when I call alloc, then UIKit calls retain/release a bunch of times, and then my release above is handled. After that, none of the retain or releases are from my code, and after popViewControllerAnimated, the count becomes -1 eventually.
If I take the release above out, things seem to work okay, so it seems the count is off by exactly one somewhere.
Any ideas?
Maybe there is something wrong with GobanVC. Do you have the implementation for it?
Because if there is a retain for every release made by UIKit (there should be).
And you say that your calls are balanced as well (one alloc and one release).
Then it means that in the implementation of GobanVC there must be something wrong.
Let's count the retains:
Alloc makes it 1.
Release makes it 0.
So, what comes out of the stack when you call pop will have retain counter equal to 0, which is NOT what you want. If you either remove release or keep it and assign "vc" to an instance variable defined as "retain" property, you will be fine.
From the code you provided I can see the following:
Root controller creates an instance of GobanVC. The GobanVC's retain count is 1.
Root controllers pushes GobanVC instance to the navigation stack. I am not sure if push increases the retain count. Most probably yes. Then the retain count of GobanVC instance becomes 2.
You release the GobanVC instance, setting its counter to 1.
Your button handler resides in GobanVC (not in the Root controller). So, GobanVC pops ITSELF from the stack with retain count 0 (because if push increases the counter, pop will decrease it). This IS a problem.
So I modified Apple's PageControl example to dynamically load various navigation controllers (along with their root view controllers) into the scroll view. I also added a technique that attempts to unload a navigation controller when it's no longer needed. I've only been at ObjC for a little over a month, so I'm not sure if I'm doing the unloading correctly. Please see my code below, followed by my questions.
First I create a mutable array and fill it with nulls, just like Apple does:
// Create dummy array for viewControllers array, fill it with nulls, and assign to viewControllers
NSMutableArray *array = [[NSMutableArray alloc] init];
for (unsigned i = 0; i <= kNumberOfPages; i++)
{
[array addObject:[NSNull null]];
}
self.viewControllers = array;
[array release];
...Later, I fill the array with UINavigationController objects like so (this is just partial code, please excuse the missing parts...the main idea is that I alloc a couple of things, assign them and then release):
id controller = [[classForViewController alloc] initWithNibName:NSStringFromClass(classForViewController) bundle:nil];
navController = [[UINavigationController alloc] initWithRootViewController:controller];
[controller release];
[self.viewControllers replaceObjectAtIndex:page withObject:navController];
[navController release];
...Finally, if a page doesn't need to be loaded anymore I do this:
[self.viewControllers replaceObjectAtIndex:i withObject:[NSNull null]];
Questions:
My understanding is that once I replace the navigation controller in my viewControllers array with null, the array releases the navigation controller. Thus the navigation controller's retain count hits zero and it no longer takes up memory. Is this correct?
What about the root view controller inside the navigation controller? Do I need to do anything with it or does it get released automatically once the navigation controller's retain count hit zero?
Thanks!
Yes. Any object put into a collection is sent a retain message. Likewise any object removed from a collection is sent a release message, the cause of the removal is irrelevant.
Yes, all objects will release all the objects it owns when they are released.
This all boils down to the simple principle of ownership that Cocoa defines:
You own the object if you received it as return value by calling a method that:
Is named alloc or new.
Contains the word copy, such as copy and mutableCopy.
You own the object if you call retain.
You may only call release and autorelease on objects you own.
You must release all owned objects in your dealloc methods.
There is just one exception; delegates are never owned. This is to avoid circular references and the memory leaks they cause.
As a side effect this also means that when you yourself are implementing a method, you must return an auto released object unless you are implementing new, or a method with copy in it's name. Objects returned as out arguments are always autoreleased.
Follow this strictly and Objective-C can be treated as if it is garbage collected 95% of the time.
Presumably yes, once your retain count reaches zero (however or whenever that happens) your object will receive the dealloc message. You can put a breakpoint to ensure that is happening. Instruments comes with a Leaks utility that should help you find memory problems, it's a great tool and I suggest using it frequently.
I'm not quite sure what you mean by "do anything to it". I presume you mean release it. The general pattern is that if you alloc or retain, you release. You can roughly guess if there is going to be a problem if your allocs and retains outnumber your releases (or vice versa, you don't want to double release).