• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Collection of Cocoa & objc questions from a "newbie"
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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.]


  • Follow-Ups:
    • Re: Collection of Cocoa & objc questions from a "newbie"
      • From: Markus Hitter <email@hidden>
    • Re: Collection of Cocoa & objc questions from a "newbie"
      • From: Art Isbell <email@hidden>
  • Prev by Date: Re: catching an option-return
  • Next by Date: Creating shared classes
  • Previous by thread: Re: Collection of Cocoa & objc questions from a "newbie"
  • Next by thread: Re: Collection of Cocoa & objc questions from a "newbie"
  • Index(es):
    • Date
    • Thread