Re: Collection of Cocoa & objc questions from a "newbie"
Re: Collection of Cocoa & objc questions from a "newbie"
- Subject: Re: Collection of Cocoa & objc questions from a "newbie"
- From: Bill Bumgarner <email@hidden>
- Date: Wed, 18 Jul 2001 00:29:32 -0400
On Monday, July 16, 2001, at 09:41 PM, email@hidden wrote:
Several curious things.
[cut question 1-- it was answered already in previous posts]
2) If I set breakpoints in the DotView custom View I see that several
methods are called twice (awakeFromNib and initWithFrame in
particular). It appears the the "self" value in the debugger has
changed so I'm guessing that one call is for the "proxy" object created
due to the nib (not that I really understand what that means, just read
it somewhere), and one is the real object that I defined and that the
proxy instantiates (???).
There aren't any proxies instantiated as a part of loading a NIB. When
a NIB file is loaded, any objects that appear as instances within the
NIB-- i.e. NOT the file's owner, but every other object in the nib--
will first be instantiated (and have the designated initializer called),
then will have all the outlets connected, then will have -awakeFromNib
invoked...
That you are seeing -awakeFromNib and -initWithFrame: invoked multiple
times with "self" changed indicates that you have multiple instances of
said objects floating around. Notably, are you loading the NIB twice?
Are there multiple instances of your DotView within the NIB file?
As much as folks would like to believe otherwise, there really is *no*
magic involved in NIB files. When a NIB file is saved, it is merely
archiving the objects as configured in Interface Builder. Loading a
NIB file is simply a process of dearchival; there are no proxies and no
real magic going on.
[To be 100% correct; magic is possible. There is a whole association
mechanism that *can* be used to implement some very complex behavior as
the object graph is restored when a NIB file is dearchived-- it is just
that it isn't typically used and, when it is, the end result is designed
to be as transparent as possible to the developer. Beyond substituting
an instance of "DotView" for the "CustomView" configured as a "DotView",
there should not be any magic in the configuration as described in (2).]
3) If I set a breakpoint in the (void)dealloc method for the DotView
custom view, it is never called (close window, quit app, never called).
Does this mean that code in a dealloc method may never be called in
general and shouldn't be counted on getting called?
This *may* be the case, but only if there is something about your
application that is causing an extra -retain to the instance of DotView.
This is a particularly trick retain/release case within the context of
the AppKit. If you have a circular object reference-- i.e. DotView
retains a controller that retains the instance of DotView-- then it is
quite likely that the application will quit (or a window will close)
without the -dealloc method ever being called!
Using an NSWindowController derived means of managing windows within
your application, this shouldn't be a problem-- the window's retain
count should drop to zero as the window is closed (as a result of
quitting the application). However, if your controller-- i.e. the
object that controls the window and its contents-- has caused a retain
cycle, then that retain cycle will not be broken simply because the
window is closed (or the app has been terminated via cmd-q/quit).
If your controller object creates a -retain'd reference to the DotView
instances and the DotView instances contain a -retain'd (via outlet or
not) reference to your controller object, you have a retain loop and
your DotView instances are *not* going to be dealloc'd when the app is
quit (or the window containing the DotView is closed).
To remedy this situation, the two most common choices are to use a
weak / non-retained reference or to implement the -windowDidClose:
delegate method and destroy the references within that method. I prefer
the latter.
A weak reference is simply a reference from object A to object B where A
does not -retain B. Obviously, if B is -release'd to the point of
deallocation without A knowing about it, there is some risk that A will
try to message B and the program will undergo catastrophic failure. The
notification center uses the notion of weak references for the observers
of notifications-- sure enough, if an observer is -dealloc'd without the
notification center being told to remove the observer, your app will
crash.
Instead, I prefer to maintain a strong reference between A and B-- in
this case, between your controller and DotView-- by retaining B/DotView
in A/controller. This "strong reference" can be cleaned up by
implementing the -windowDidClose: delegate method. NSWindow will
invoked said method after it has been determined that the window really
should close and already did close-- that is, said method is an ideal
spot for your application to clean up any infrastructure used internally
to maintain state associated with the window.
In this particular case, you likely implement a -windowDidClose: method
that would -release the DotView reference(s) after the window closed.
Very likely, this would cause the DotView instance's retain count to
drop to 0 and, as such, cause the -dealloc method to be invoked.
If not, then there is still some kind of a circular reference between
instances of DotView and other classes.... clean those up on the
-windowDidClose: and it will be dealloc'd.
---
The bottom line is this:
Any time an object is not deallocated as a part of normal application
termination (or window closure, if it is a window related object), it is
because your application has created a -retain'd reference to said
object that your code did not -release. Figure out where the -retain
happened and figure out where the most appropriate place to -release it
is, and you will have fixed the problem.
Throwing in a -release at random to fix the problem is only asking for
trouble. Never ever ever write a call to -release or -autorelease
without understanding where the -retain came from. To do so is to ask
for *hours* of painful debugging trying to figure out why your app
crashes in odd circumstances or leaks memory like a sieve.
Finally, even if you DO perfect the balance between -retain/-release and
related, there is no guarantee that the -dealloc method WILL be called
on your object; at any time, the user can hit cmd-opt-esc and kill
your application. This particular kill signal is of the type that is
nearly impossible to catch and even more difficult to react to in a
fashion that will exhibit consistent and manageable behavior.
b.bum
[BTW: Aaron Hillegas has written an *awesome* programming Cocoa book
that covers a lot of the subtleties of dealing with issues like
these.... it should be out sometime this fall.]