initWithCoder vs awakeFromNib when instantiating UIViewControllers from UIStoryboards.

After poking around on Stackoverflow, reading the docs, perusing a few blogs, and thinking about it a bit while still having to make a decision so I can keep progressing, I've decided to throw all my view controller initialization into awakeFromNib by default. I'll use initWithCoder if any setup must happen in order for initialization in awakeFromNib to succeed.

My reasons.

  • Various init... methods may get called on different objects depending upon if they conform to the NSCoding protocol or not, but the one common method is awakeFromNib. Thus, it gives greater consistency.
  • Outlets and other attributes defined in the archive are not available during initWithCoder, and messages cannot be sent to other objects in the archive. During awakeFromNib, the opposite is true. Thus, it is simpler because I don't have to think about which of these categories into which my code falls.
  • This is silly, but subclassing is easier with awakeFromNib than initWithCoder – no arguments to pass to super, and I don't have to deal with self.

Update – problems I have run into so far.

  • The view controller's view is not loaded yet in awakeFromNib, which makes perfect sense, since viewDidLoad would have been called already if it had. If the view is not loaded, then of course, none of its subviews are either. However, a frequent practice is to have IBOutlets in the view controller point to subviews (buttons, controls, so on) of the top level view. So while the IBOutlet properties exist in awakeFromNib, but in this case all of their values will be nil and are thus useless.
  • View controllers are instantiated by some parent object. And if that parent object is instantiating a view controller from a storyboard, then quite frequently, certain properties of the soon-to-be-instatiated view controller are set by the parent object in prepareForSegue. Any properties relying on values passed in from prepareForSegue have not yet had those values passed in during awakeFromNib.

Initial conclusion.

So where does this leave me? Well, due to the situations just described, I already have a few UIViewController subclasses that can only get their initialization completely done (and only done once) by putting the code in viewDidLoad and checking a BOOL ivar flag to see if the code has already run. So why not just put all the initialization code in that conditional block in viewDidLoad? Well, I guess I have to keep awakeFromNib in order to set my flag ivar to YES.

Later conclusion.

Well, trying to put all of my one-time initialization code solely in viewDidLoad wrapped in a conditionally executed block did not work or make best sense in all cases. I do not want to be foolishly consistent, so I put some stuff in awakeFromNib and some in viewDidLoad, and in one case inside a one-time conditional block in viewWillLayoutSubviews.

Sometimes being a neat (consistency) freak can prevent problems, and other times it can give me pointless work. I try to anticipate the latter to prevent doing that to myself, but I'm not always successful.

No comments:

Post a Comment