tags:

views:

57

answers:

1

I keep running into situations with UIViewControllers containing a large amount of IBOutlets connecting the controller to its view's subviews (typically UILabels).

Following "best practices", i.e. use retain on all UI elements: @property (retain, nonatomic) UILabel *theElement1, @property (retain, nonatomic) UILabel *theElement2, ... gives me insane amounts of boiler plate code in dealloc and viewDidUnload for the view controller.

The offending IBOutlets are never used nor set outside the UIViewController (the set-method is only used in viewDidUnload and when the nib is loaded) except automatically when the nib is loaded.

The result from "best practice" is:

  • dealloc littered with [theElement1 release], [theElement2 release] etc.
  • viewDidUnload with [self setTheElement1:nil], [self setTheElement2:nil] etc.

However, since all of those elements are known to be retained by the view anyway, and the view is released by the UIViewController at appropriate times, I see absolutely no reason whatsoever for me to manually manage this.

The reason for this particular "best practice" (as far as I can tell) is to be consistent with your retains. But once you start having a large amount of outlets, you're more likely to miss handling the some outlet somewhere in either of the two methods, than you'll have trouble correctly changing outlets to "retain" for those special outlets you really want to retain even after the view is goodbye.

Is there some reason for this "best practice" other than the one I know about or should I feel quite free to break this "rule" in the particular case of subviews to an UIViewController's view?

+3  A: 

You should stick to this best practice. It protects you from very bizarre crashes when you access IBOutlets after a memory warning. Yes, you need to manually manage your IBOutlets. Accessorizer does a nice job of automating this code.

Before ObjC 2.0, we had to write all of our accessors by hand, too (@property and @synthesize are very new additions to the language). Things have gotten a lot nicer. As we move to the 64-bit ABI and garbage collection, things get even simpler (and you should expect these things eventually to make their way to iPhone).

But for now, follow the memory management rules as laid out in Memory Management of Nib Objects. You trade a really small amount of typing for a huge amount of debugging. (Hmm, looks like they've updated this doc again; time to study up myself.)

Rob Napier
Nice -- I hadn't seen that Nib Memory Management document before.
Nathan S.
My only other sane alternative after a while becomes to generate UI elements by hand and stick them into an NSArray. But that's like waving a flag and giving up.Having something like 20-30 IBOutlets is a maintenance nightmare. And why would I have so many outlets you might ask? Localized UILabels is my answer.
Nuoji
Other than making it harder to debug bugs involving these fields, what are the drawbacks? In fact, for these particular fields you could forgo the whole @property/@synthesize and make those fields @private to prevent any other classes from mistakenly believe that they may be used even when the view deleted.I'm talking in the very specific context of UIViewControllers and (read-only) subviews to that controller's view. The problem with a huge amount of IBOutlets does not seem particularly likely to arise in other.
Nuoji
If you have 20-30 labels, I'd just localize the NIB rather than the labels. The problem isn't others messing with your outlets, it's the view controller modifying the outlet (to install the correct label) when the label has been released. You're going to have to re-plug those labels every time you reload due to memory warning. And if you aren't very careful, you'll be modifying our IBOutlet pointer when it's released and you'll crash. When I say "difficulty of debugging" I mean "crashes in the field when memory is tight and you don't know why."
Rob Napier
Unfortunately it's not always possible or practical to localize in the nib. Please elaborate what you mean by "the view controller modifying the outlet". Are you pointing to accessing these fields between viewDidUnload (in response to memory restraints) and viewDidLoad (restoring the controller and view)?
Nuoji
Correct. By "modifying the outlet" I mean that you're calling [label setText:] at some point or you wouldn't need this IBOutlet. If you do not retain label, it will point to freed memory at certain points. You can try to get away with it based on belief that you know those points, but the cardinal rule of ObjC memory management is that you retain what you care about, not hope that someone else is retaining it for you. I encourage you to localize these NIBs (not all NIBs, but the ones with lots of text). This is exactly the problem that's designed to solve. Why is it not possible or practical?
Rob Napier
The point is that you get enormous amounts of boilerplate. With the new ABI it's a bit better, since you don't need to synthesize everywhere. Unfortunately, when you don't synthesize, you need to do the (somewhat problematic) [self setTheOutlet:nil] in dealloc (although this *does* reveal when you haven't properly removed observers... )The advantage is that with the new ABI and no synthesize, the dealloc and viewDidUnload lines are pretty much identical, which alleviates some of the pain. Furthermore not having to synthesize everything removes another piece of boilerplate.
Nuoji