16

I'm an experienced iOS dev and I have decided to try my hand at some AppKit development. There are a couple of adjustments that I am making API-wise, but otherwise am finding OS X development to be, shall we say, 'familiar'.

I've been building my AppKit UIs in Interface Builder and noticed that when I use the WYSIWYG editor to create properties in my code files, Apple is creating the following:

@property (assign) IBOutlet NSTableView *tableView;

I find this very curious because the default way of doing things in iOS would have led me to do this:

@property (nonatomic, retain) IBOutlet NSTableView *tableView;

I realize that in Mac development I don't have the same memory constraints that I do on mobile, where a view could get unloaded and there may be a need for strong references to UI elements.

In the AppKit case I can pretty well assume that my UI elements will always be there unless I fiddle with the view hierarchy and remove it from its parent view. It would seem prudent to have a strong reference at all times in order to guard against unintentionally accessing dangling pointers.

Why is Apple creating a weak reference here, instead of a strong one?

Am I setting myself up for some unintended consequence by using strong references (but properly releasing in dealloc)? Is there some pattern here that I am missing?

Wayne Hartman
  • 18,369
  • 7
  • 84
  • 116

1 Answers1

16

As File's Owner, you should own any and all top-level objects in the nib. You generally do not need to own any objects within those objects, because a parent object will own its child objects; for example, a window will own its views.

AppKit's nib loader implicitly retains all top-level objects on behalf of the File's Owner. (This made sense before @property, synthesized accessors, and ARC existed.) Thus, even if the relevant properties are weak or unsafe_unretained (the latter being a synonym for assign), the owner will in fact own the top-level objects. And if you go the other way and make those properties strong (a.k.a. retain), then the FO has two ownerships of each object: the implicit ownership, and the strong-property ownership.

Assuming you're using manual reference counting, you can release the implicit ownership in awakeFromNib, but that's just made work. As long as you're not going to replace any of those objects after the nib is loaded (e.g., swap out a table view for another table view), an unsafe_unretained property will work just fine without a superfluous retain or any made work.

unsafe_unretained is named that (and that name is preferred over assign for object properties) for a reason, though. Returning the window-and-its-views example, suppose you own a window and know about one of its views. The view's superview is probably its only owner, so, when you close the window (or the user closes it), the view will get released and consequently deallocated. If your property to the view is unsafe_unretained/assign, you still know about this now-dead object, and trying to send a message to the view may cause a crash or an exception.

You should switch to ARC and declare the property as weak. That way, no redundant ownership is created, and the property will automatically be set to nil when the view dies, preventing an over-release crash from ensuing.

(If you're not the File's Owner, none of that applies to you and you should probably declare your properties as you see fit. Either weak or strong may be a good choice, depending on how you see your ownership hierarchy and what kind of object you're referencing.)

On iOS, UIKit's authors took out the now-problematic implicit retain. You are expected to write your own ownerships; if you mean to own an object from a nib or storyboard, you write a strong property, and if you mean only to know about it, you write a weak or unsafe_unretained one, exactly as you'd expect.

TL;DR: Hysterical reasons.

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • 1
    I was afraid that was going to be the answer, but very well explained, nonetheless. – Wayne Hartman Aug 30 '12 at 04:25
  • So we'd still need a `[tableView release]` in `dealloc`? If so, when does that `release` get called when using ARC? – MrMage Aug 31 '12 at 15:30
  • @MrMage: You ordinarily wouldn't own a table view, since you'd normally put it into a window or container view in the nib, not have it at the top level of the nib. You'd own the window or top-level view, and simply know about the table view. Anything you do own, you must release. – Peter Hosey Aug 31 '12 at 19:54
  • @MrMage: Under ARC, that `release` message cannot exist: `release` messages are illegal (as are `retain` and `autorelease`). This is largely because ARC releases your strong references for you; if you were allowed to do it yourself, it'd be much harder for ARC to think around what you're up to. (This is also why it complains about `performSelector:`, and why `@selector(release)` is also illegal.) One consequence of no `release`s is that you seldom need to write `dealloc`—any implementation that would consist entirely of `[x release]` messages simply goes away, done for you by the compiler. – Peter Hosey Aug 31 '12 at 19:58
  • 1
    @MrMage: As for countering the implicit retain (which ARC doesn't know about, and so won't balance out), the documentation I linked to in the answer suggests using `CFRelease`. It's totally cheating (going behind ARC's back), but the only way to deal with the nib loader's implicit retain under ARC. – Peter Hosey Aug 31 '12 at 20:03