Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

BadWolf13

macrumors 6502
Original poster
Dec 17, 2009
271
0
One of the windows in my program isn't releasing when I close it. According to the Allocations tool in Instruments, the window controller still exists after the window is closed. The "Release When Closed" option is checked in Interface builder, and garbage collection is "required," so I can't figure out what's going on. Anyone got any ideas?
 
A window controller isn't the same as the window itself. They are separate objects.

How are you creating the window controller and storing its reference?
 
A window controller isn't the same as the window itself. They are separate objects.

I'm aware of that, but given their connection, don't they generally release at the same time?

How are you creating the window controller and storing its reference?

Creating it with these lines;

Code:
	if (!findContactController) {
		findContactController = [[FindContactController alloc] initWithPersistentStoreCoordinator:[self persistentStoreCoordinator]];
	}

Not sure what you mean by "storing its reference." What exactly does that mean?
 
What ever class that code's from has a findContactController instance variable, I presume. That is storing a reference to the controller object, preventing it from being garbage collected. The window controller will stick around until findContactController is set to nil or another instance of FindContactController.

BTW According to the docs for isReleasedWhenClose in NSWindow, release when closed is ignored for windows owned by window controllers.
 
What ever class that code's from has a findContactController instance variable, I presume. That is storing a reference to the controller object, preventing it from being garbage collected. The window controller will stick around until findContactController is set to nil or another instance of FindContactController.

Thank you, but I find one flaw in that theory. The windows created and stored with;

Code:
	ContactWindowController *newWindowController = [[ContactWindowController alloc] initWithNewContactInManagedObjectContext:managedObjectContext];

	[[self windowControllers] addObject:newWindowController];

Are getting released when they're closed. They both have window controllers, the main difference being that one is stored in an array of window controllers, and one is just one of a kind. Is there something else that I'm missing?
 
Thank you, but I find one flaw in that theory. The windows created and stored with;

Code:
	ContactWindowController *newWindowController = [[ContactWindowController alloc] initWithNewContactInManagedObjectContext:managedObjectContext];

	[[self windowControllers] addObject:newWindowController];

Are getting released when they're closed. They both have window controllers, the main difference being that one is stored in an array of window controllers, and one is just one of a kind. Is there something else that I'm missing?
His "theory" is correct. Since you never clear out the reference, the window controller never gets collected.

NSDocument most likely is handling the closing of the window and remove its corresponding window controller from the array.

Since you're managing the controller yourself you need to do the same and set the reference to nil for it to collect.

With GC off you'd still need to do the same thing, only thing different is you'd be calling release/autorelease in addition.
 
I'm glad for the advice, but I think you're misunderstanding a few things. This is not a document based app, so there's no NSDocument to speak of. In addition, the window controller in the array is the one that IS getting released. The one that's solo is the one that's not being released. I'm not sure what other information I can give to help.
 
Wait, there is something else to note. There's a second window, an NSPanel in the same NIB file, which gets called as a sheet. Could that mess this up?
 
Okay, let's see we can get onto the same page.

Is the first code snippet you gave in ContactWindowController?

Where is findContactController declared?

Is it the find contact controller that's not being released?

Is findContactController assigned anywhere else is in your code other than in the code snippet given?
 
Is the first code snippet you gave in ContactWindowController?

No, it's in the AppDelegate, as is the other snippet.

Where is findContactController declared?

In the header for the AppDelegate.

Is it the find contact controller that's not being released?

Yes.

Is findContactController assigned anywhere else is in your code other than in the code snippet given?

No.
 
Then I don't see how my original response doesn't stand.

An object cannot be garbage-collected while any other object has a (strong) reference to it.

Your code will allocated and assign a find contact window controller to the findContactController instance variable in your application delegate. From that point on there is always another object with a reference to the find contact window controller, that being your application delegate.

From my perspective, you'd need code that will set your app delegate's findContactController to nil when the find contact window is closed. This is not just for garbage-collection. Try opening the find contact window, closing it, and then try opening it again. I theorise that it won't open because findContactController is still set.

I can't explain off the top of my head why the controllers in your array are being released. How do you know that they are?
 
From my perspective, you'd need code that will set your app delegate's findContactController to nil when the find contact window is closed.

Well that would be very good. Considering the importance of reducing memory leaks, and the common use of opening and closing windows in an application, I would think that there is some boilerplate code out there that does just this. For that matter, the method I use to create and store the window controller is what is used in the books I've learned from, if you know of a better way to create and store windows in your program, then by all means, please share.

This is not just for garbage-collection. Try opening the find contact window, closing it, and then try opening it again. I theorise that it won't open because findContactController is still set.

Actually, it does reopen. The reason for that is likely the line below that snippet of initialization.

Code:
	[findContactController showWindow:self];

I can't explain off the top of my head why the controllers in your array are being released. How do you know that they are?

Simple, I watch the allocation in Instruments. It shows a decrease in both memory usage and the number of living instances of ContactWindowController. This is the reason I am reluctant to completely change the way I create and store the window controllers is the fact that it works in that case.
 
I don't think the way you're creating and storing the find contact controller is incorrect. As you demonstrate, even if the window is closed, reusing the old controller is still working.

I'll get shot by the purists here, but I think that not all memory leaks are a problem.

If you repeatedly allocate memory that never gets freed so that over time your applications memory footprint continually increases, you have a problem. If you just create one smallish object over the lifetime of a program and it never gets released, then I think you need to weight up the constant footprint increase with the effort to reduce the footprint.

In this case, if your find contact controller isn't large and doesn't reference other large objects, then the first time your user brings up the find contact window, your application's footprint will increase, but then remain constant. It won't keep increasing over time, even with repeated openings of the window. (Assuming no other memory leaks).

And you might actually have the potential advantage that if you don't dispose of the window, the window can be in exactly the same state between uses.

If you want to clean up, put the clean up code in -windowWillClose: of your window's delegate, which is most likely your controller.
 
Well, when I open the window for the first time, my program's memory usage jumps by 0.5MB. I don't know, is that a lot for a program that will likely be running for a few hours?

The clean up code is the real kicker. I'm not sure how to do that. I mean, I can't just put [self dealloc] in the windowWillClose method. Can I?
 
No you can't, but that's because you should never explicitly send dealloc to an object. You need to release the remaining reference to your controller in your app delegate by setting it to nil.
 
Well, it's less than ideal, but I used the WindowDidClose notification to set the window controller to nil if it matches the class. At the point that I close the window, the # living in Instruments changes from 1 to 0. I would have thought that means that it's released, but I have breakpoints in the -(void)finalize method of my window controller, but the program is never hitting that breakpoint. So what's going on here?
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.