Why doesnโ€™t the release message appear in the NSView when the Cocoa application terminates?

Short version:

  • Why don't NSView subviews send a release message when a Cocoa application terminates?
  • Is there any way to undo this behavior?

Example:
The MyView class shown below is nothing more than a subclass of NSView , which is sent to the console when it is created and destroyed. I checked it and found that it was working correctly. However, when I use it, as shown in the following code snippet from my application delegate, I see something unexpected (see Sample output).

 // MyView: @interface MyView : NSView { } @end @implementation MyView - (id)initWithFrame:(NSRect)frameRect { if ((self = [super initWithFrame:frameRect]) == nil) { return nil; } NSLog(@"init %@", self); return self; } - (void)dealloc { NSLog(@"dealloc %@", self); [super dealloc]; } @end 

 // Application delegate: - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { NSLog(@"begin"); parentView = [[MyView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; MyView * myView = [[MyView alloc] initWithFrame:NSMakeRect(10, 10, 80, 80)]; [parentView addSubview:myView]; [myView release]; NSLog(@"run"); } - (void)applicationWillTerminate:(NSNotification *)aNotification { NSLog(@"quit"); [parentView release]; NSLog(@"end."); } 

This application displays the following result:

begins
init < MyView: 0x10013f840 >
init < MyView: 0x10261b620 >
run
quit
dealloc < MyView: 0x10013f840 >
end.

Problem:
I can clearly see that the first view object is freed when the application exits, and I am sure (checked and verified) that NSView objects automatically free their routines when they themselves are freed. However, it seems that during the termination of the application, these subclauses are not freed.

Long version: (AKA, why, in fact, will anyone care? :)
Let me start by saying that I am familiar with how memory is freed by a running application when it exits. I know that my subtitles will be disposed of correctly even if they are never sent a release message, so I donโ€™t worry about it being a leak. In fact, I'm sure (but not 100% sure) that the answer to my question is No. 1: "Because the release of subqueries is not required when the application is about to end."

I am using some simple manual code to track memory while my application is in debug mode. I make calls to the Trace_Init() and Trace_Dealloc() methods in the init and dealloc methods of all my custom classes, and I use the atexit() function to report any unreleased objects after the Cocoa part of my application is completed. I find this a lot easier than regularly using the tool to improve Apple's memory performance. If I cause a memory leak during operation, I will find out about it as soon as my application closes.

However, the absence of a call to dealloc during termination means that any of my custom NSView subclasses that are used as NSView are detected as a memory leak when exiting the application. So the reason for my question is # 2. I would like Cocoa to free everything at completion so that memory tracking can end normally. Naturally, I would only cancel the default behavior in debug mode. None of the memory tracking code is included in my released application, and it should be able to exit it as efficiently as usual.

What is it! (phew) If you made it this far, thanks for taking the time to read it all.

+6
memory-management objective-c cocoa nsview
source share
3 answers

I get it. The solution was to create and release my own NSAutoreleasePool as part of the applicationWillTerminate: method.

Details:
Deep in the bowels of the NSView dealloc , all kinds of actions are performed to remove the view and all its subzones from the responder chain, configure the next key type, send delegate messages, etc. Somewhere in this code, each subview sends a retain message, and then an autorelease message is autorelease . (In fact, each subtask is saved and auto-implemented twice - see Details below). This is normal, but here is a kicker: when autorelease messages are sent to autorelease , they are added to the fact that NSAutoreleasePool is active at this point in time, and they remain until this particular pool disappears from the volume. If the application terminates, the pool to which they are added is the one that is created automatically during each iteration of the main event cycle of the application, and this pool is never sent a release message, because the application is about to leave!

Experimental Results:
I added a few log messages to the init , retain , release and autorelease for MyView , which have code similar to this:

 NSLog(@"[%@ retain]: count = %d", [self name], [self retainCount]+1); return [super retain]; 

I also posted { } around the code for dealloc so I can see when the magic happens.

Using these posting messages, here's what happens to my NSView objcts:

 begin [parent init]: count = 1 [subview init]: count = 1 [subview retain]: count = 2 [subview release]: count = 1 run quit [parent release]: count = 0 [parent dealloc] { [subview retain]: count = 2 [subview autorelease]: count = 2 [subview retain]: count = 3 [subview autorelease]: count = 3 [subview release]: count = 2 } end. 

Now when I use the following code in applicationWillTerminate:

 - (void)applicationWillTerminate:(NSNotification *)aNotification { NSLog(@"quit"); NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; [parentView release]; [pool release]; NSLog(@"end."); } 

The result is as follows:

 begin [parent init]: count = 1 [subview init]: count = 1 [subview retain]: count = 2 [subview release]: count = 1 run quit [parent release]: count = 0 [parent dealloc] { [subview retain]: count = 2 [subview autorelease]: count = 2 [subview retain]: count = 3 [subview autorelease]: count = 3 [subview release]: count = 2 } [subview release]: count = 1 [subview release]: count = 0 [subview dealloc] { } end. 

And you can clearly see the two release messages sent to the subview using NSAutoreleasePool when it drains.

Literature:
NSView.m by GNUStep
Autorelease pools from Apple Developer documentation

+5
source share

These are not just looks. It's all. I do not think that even an NSApplication object is freed.

In fact, I'm sure (but not 100% sure) that the answer to my question is No. 1: "Because the release of subqueries is not required when the application is about to end."

I think so too.

If you want your custom object graph to be released upon exit, let your application delegate belong to it and release other top-level objects in applicationWillTerminate :. As long as you properly manage all your owners and you release each individual top-level user object from this method, all your user objects, including views, will die.

Note. I have not tried mixing this with Core Data. It may or may not be possible to do this for your managed entities. I have no first-hand experience.

+3
source share

In the code above, you add a subview to ivar named 'view'. Is this what you actually did, or is it just from copying code to a question?

I ask about this because if I create an IBOutlet to represent the contents of the main window and run my code, it does what you say. But if I add myView local var to parentView, it will free:

 begin init <MyView: 0x174460> init <MyView: 0x174770> run quit dealloc <MyView: 0x174460> end dealloc <MyView: 0x174770> 

Furthermore, the routines seem to get autoreleased (adding a log message to autorelease proves it).

+1
source share

All Articles