I'm currently learning objective-c and I'm currently training with NSTableView.
Here is my problem :
I have linked my tableview to my controller through Interface Builder so that it has a datasource, I have implemented NSTableViewDataSource protocol in my controller and I have implemented both -(NSInteger) numberOfRowsInTableView: and -(id) tableView:objectValueForTableColumn:row: methods.
I have created a raw business class ("person") and I succeeded to display its content into my NSTableView.
But then, I put some NSLog in my dealloc methods to see whether the memory was freed or not and it seems that my array as well as my "person" instances are never released.
here is my dealloc code in the controller:
-(void)dealloc
{
NSLog(#"the array is about to be deleted. current retain : %d",[personnes retainCount]);
[personnes release];
[super dealloc];
}
and in my "person" class
-(void) dealloc
{
NSLog(#"%# is about to be deleted. current retain : %d",[self prenom],[self retainCount]);
[self->nom release];
[self->prenom release];
[super dealloc];
}
When these deallocs are supposed to be called in the application lifecycle? Because I expected them to be called at the window closure, but it didn't.
In the hope of beeing clear enough,
Thanks :)
KiTe.
I’m assuming you’re never releasing the window controller object that owns the (only) window. As such, the window controller and every top level object in the nib file are retained throughout the application lifecycle, including the window (and its views).
Since the window controller exists throughout the application lifecycle, it isn’t released, hence its -dealloc method is never called. And, since the controller -dealloc method is never called, its personnes array isn’t released.
The personnes array owns its elements. Since the array isn’t released, neither are its elements, hence the -dealloc method of the corresponding class/instances is never called.
Don't ever use retainCount. The results are misleading at best. If you practice proper memory management practices, you'll be fine. Have you had any memory issues/crashes?
Related
We are subclassing UITabBarController as well as UITabBarControllerDelegate to handle certain events concerning tab switches.
Now in our custom tab bar controller we have:
- (id)initCustomTabBarController {
self = [super init];
if(self) {
[self setDelegate:[[CustomTabBarControllerDelegate alloc] init]];
// ...
}
return self;
}
Since we transitioned the project to ARC, the delegate is released to early which causes a tab switch run into a deallocated instance.
The property is defined as assign in UITabBarController.h - which I obviously have no influence on.
What can I do to make the delegate object "live" longer than for the init method?
The way you have done it, it is expected that the delegate will not outlive the object, because it is weak. Remember, you created the object, it's up to you to hold on to it.
However - the pattern that you are using is incorrect.
The point of a delegate, is that it provides method implementations to a class that a class can't add for itself, because it doesn't have enough information. For example, a table view delegate. A table view, in order to be generic, cannot know how many rows or sections to display, so it asks it's delegate to supply this information.
In your case, you have an object that is creating it's own delegate. In which case, why bother having a delegate at all? Just implement the methods in the class.
Yes this would be normal under ARC, since no reference to it is made (aka strong propteries) it should be released at the end of cycle.
Just make a property in the class where you assign the CustomTabBarControllerDelegate take make it strong. Then assign this property to the delegate.
In non ARC the way you have set it up you could have create a memory leak.
I wrote the following piece of code:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
GameViewController *gameViewController = [[GameViewController alloc]initWithLevelNumber:([levelGroup intValue]*100+indexPath.row) Bonus:NO];
NSLog(#"Retain Counter =%d",gameViewController.retainCount);
[navController pushViewController:gameViewController animated:YES];
[gameViewController release];
NSLog(#"Retain Counter=%d",gameViewController.retainCount);
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
The results of the two logs are, in sequence 1 and 6!
How this is possible? I only call the alloc method one time and release after push the controller on the stack.. alloc-> +1, push-> +1, release-> -1 = 1 or not?
I'd like the view controller is dealloc'd when i pop it off the stack..
Please read this note to be clear in this question. It is part of NSObject Protocol Reference:
Important: This method is typically of no value in debugging memory management issues. Because any number of framework objects may have retained an object in order to hold references to it, while at the same time autorelease pools may be holding any number of deferred releases on an object, it is very unlikely that you can get useful information from this method.
NSObject Protocol Reference. RetainCount discussion
Autorelease your GameController creation, like this:
GameViewController *gameViewController = [[[GameViewController alloc]initWithLevelNumber:([levelGroup intValue]*100+indexPath.row) Bonus:NO] autorelease];
Then delete [gameViewController release]; Then your code looks kosher, and gameViewController will be autoreleased after being popped from the nav stack. Don't worry about retainCount - when you push a view controller, UIKit takes over and will retain/release the thing as needed. You just have to worry about your code. Actually, the way you have it written should be fine, I just think my suggestions here make the code cleaner.
Unless you see in Instruments that you have a memory leak of your gameViewController object, I think you needn't worry.
It's because there is some retain internally (by pushViewController: method), you should not check the retain count, only check that you release the object you own, especially when you check retain count between sdk call methods.
Do you use NSNotificationCenter in your GameViewController?
May be you add your view controller as observer to NotificationCenter and it increase retainCount.
I have a general question regarding memory management of properties. Currently, I always use properties without any explicit declaration of related ivars. And, for every retained or copied property I'm releasing its retain count both in dealloc and viewDidUnload methods:
-(void)dealloc{
[self.myProperty release];
[self.myOutlet release];
[super dealloc];
}
- (void)viewDidUnload{
[super viewDidUnload];
self.myProperty = nil;
self.myOutlet = nil;
}
Now, I know that only the outlets and properties retained by the main view should be set to nil in viewDidUnload, and the rest properties should be released in dealloc. But hey, why do I have to bother for every property where it must be released - in dealloc or in viewDidUnload? If some property will be released twice it's OK because it wouldn't crash the app by sending a message to nil object. Putting release in both places (dealloc and unload) saves time and prevents from bugs later when doing code refactoring and forgetting to change release place. Any critics and shouting on that? :)
If you're using property accessors in -dealloc because you don't have access to the ivar directly, you should do the same in -dealloc that you do in -viewDidUnload:
self.myProperty = nil;
The point of using -release in -dealloc is to avoid calling the accessor, which conceivably could have been overridden by a subclass to have side effects that you don't want in -dealloc, when everything else from the subclass has already been deallocated. But if you're already calling the accessor in -dealloc, you might as well use the setter to release the ivar and ensure that it's done right.
The difference between -dealloc and -viewDidUnload is that you're still working with a complete, fully functional object in -viewDidUnload, whereas the object may already be partially deallocated in -dealloc.
My previous answer on this discusses what Apple recommends and why. Relevant portions reproduced here for clarity:
Also, from the Apple docs on -viewDidUnload:
The preferred way to relinquish ownership of any object (including those in outlets) is to use the corresponding accessor method to set the value of the object to nil. However, if you do not have an accessor method for a given object, you may have to release the object explicitly
So, there you go. If your outlet has a property associated with it (which they all should anymore), then nil it in -viewDidUnload -- but don't release it. This makes sense when you consider what is actually happening in a synthesized accessor; the code looks something like this:
- (void) setMyView1 : (UIView *) view {
if (myView1) // the associated IVAR is already set
[myView1 release];
myView1 = [view retain];
}
As you can see, setting a synthesize property to nil implicitly releases the retained object.
Also from the docs in regards to -dealloc:
If you implement this method but are building your application for iOS 2.x, your dealloc method should release each object but should also set the reference to that object to nil before calling super.
Unless you are supporting iOS2.x, there is no need to set objects to nil in dealloc.
So, to summarize Apple's docs regarding -viewDidUnload and -dealloc:
In -viewDidUnload, nil properties (including IBOutlet properties), but don't release them
In -dealloc release properties, but don't nil them (unless building for 2.x).
I've only recently noticed a crash in one of my apps when an object tried to message its delegate and the delegate had already been released.
At the moment, just before calling any delegate methods, I run this check:
if (delegate && [delegate respondsToSelector:...]){
[delegate ...];
}
But obviously this doesn't account for if the delegate isn't nil, but has been deallocated.
Besides setting the object's delegate to nil in the delegate's dealloc method, is there a way to check if the delegate has already been released just incase I no longer have a reference to the object.
No. There is no way to tell whether a variable points to a valid object. You need to structure your program so that this object's delegate isn't going away without letting it know first.
I assume you're not using GC. In that case, standard convention is that the code that sets the delegate is responsible for setting the delegate-user's reference to nil before allowing the delegate to be deallocated. If you're using GC, you can use a __weak reference for the delegate, allowing the garbage collector to set the reference to nil when the instance is garbage collected.
how about using a counter that you increment everytime you alloc and decrement everytime you dealloc. That way you could detect double allocs and could decide not to use a delegate if the counter isnt nil but the address isnt nil also
for debug proposes you can override release method on your class to see when it is called.
-(oneway void)release
{
NSLog(#"release called");
[super release];
}
I have a couple of questions relating to UIViewController:
1) When are each of the methods called for UIViewController? Specifically, the difference between viewDidLoad, viewDidUnload, and dealloc.
2) What's the difference, in general, when setting a pointer equal to nil and releasing the pointer? I know that in viewDidUnload you're supposed to set it equal to nil but in dealloc call release.
UPDATE: Sorry, just realized the question is misleading. Instead of dealloc, I meant -- when is initWithNibName:bundle: and release called? Just once by IB, right?
Setting a pointer to nil doesn't release the memory that it points to.
When you do something like
self.pointer = nil;
it's usually a case that the property has a retain attribute. When this is the case, setting the property to nil will indirectly cause a
[pointer release];
pointer = nil;
In the case of the view controller methods, viewDidLoad is called when your view is loaded, either from a nib, or programatically. More specifically, it's called just after -loadView is called. You shouldn't need to call loadView manually, the system will do it. The viewDidUnload method is called in the event of a memory warning and your view controller's view is not onscreen. Subsequently, loadView and viewDidLoad will get called again on demand.
The dealloc method, as normal, is called when your object's retain count reaches 0.
pointer = nil; // just clears the variable in which you store the pointer, but does not free memory.
[pointer release]; // just frees the object (memory), but does not clear the variable used to point to it.
self.pointer = nil; // sets the variable to nil. Also releases the object ONLY if pointer is a #property(retain) ivar.
One easy way to see when various methods are called is to do this in your UIViewController:
- (void)viewDidLoad
{
NSLog(#"MyViewController::viewDidLoad");
[super viewDidLoad];
// the rest of your viewDidLoad code, here.
}
// Etc., for the other methods of interest.
NOTE: much can be gleaned from overriding retain & release to log and then following along in the debugger.