views:

1581

answers:

4

Anyone know why this root View Controller's viewDidLoad is being called twice at launch? It's driving me nuts!

here's the stack trace from first time through viewDidLoad:

#0  0x0000276a in -[RootViewController viewDidLoad] at RootViewController.m:71
#1  0x3097548f in -[UIViewController view]
#2  0x00002734 in -[RootViewController initWithCoder:] at RootViewController.m:39
#3  0x30ab5ce4 in -[UIClassSwapper initWithCoder:]
#4  0x30514636 in _decodeObjectBinary
#5  0x30514035 in _decodeObject
#6  0x30ab5a1d in -[UIRuntimeConnection initWithCoder:]
#7  0x30514636 in _decodeObjectBinary
#8  0x30515f27 in -[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:]
#9  0x305163b0 in -[NSArray(NSArray) initWithCoder:]
#10 0x30514636 in _decodeObjectBinary
#11 0x30514035 in _decodeObject
#12 0x30ab4dde in -[UINib instantiateWithOptions:owner:loadingResourcesFromBundle:]
#13 0x30ab6eb3 in -[NSBundle(NSBundleAdditions) loadNibNamed:owner:options:]
#14 0x308f85f1 in -[UIApplication _loadMainNibFile]
#15 0x30901a15 in -[UIApplication _runWithURL:sourceBundleID:]
#16 0x308fef33 in -[UIApplication handleEvent:withNewEvent:]
#17 0x308fad82 in -[UIApplication sendEvent:]
#18 0x309013e1 in _UIApplicationHandleEvent
#19 0x32046375 in PurpleEventCallback
#20 0x30245560 in CFRunLoopRunSpecific
#21 0x30244628 in CFRunLoopRunInMode
#22 0x308f930d in -[UIApplication _run]
#23 0x309021ee in UIApplicationMain
#24 0x000022e4 in main at main.m:14

and the second time:

#0  0x0000276a in -[RootViewController viewDidLoad] at RootViewController.m:71
#1  0x30ab50cd in -[UINib instantiateWithOptions:owner:loadingResourcesFromBundle:]
#2  0x30ab6eb3 in -[NSBundle(NSBundleAdditions) loadNibNamed:owner:options:]
#3  0x308f85f1 in -[UIApplication _loadMainNibFile]
#4  0x30901a15 in -[UIApplication _runWithURL:sourceBundleID:]
#5  0x308fef33 in -[UIApplication handleEvent:withNewEvent:]
#6  0x308fad82 in -[UIApplication sendEvent:]
#7  0x309013e1 in _UIApplicationHandleEvent
#8  0x32046375 in PurpleEventCallback
#9  0x30245560 in CFRunLoopRunSpecific
#10 0x30244628 in CFRunLoopRunInMode
#11 0x308f930d in -[UIApplication _run]
#12 0x309021ee in UIApplicationMain
#13 0x000022e4 in main at main.m:14
A: 

Weird. I haven't seen this particular case, but in general, you ought to assume that viewDidLoad can be called multiple times. It'll get called whenever a nib file that references that controller gets loaded.

For a simple app with only one nib, that shouldn't happen. But in a more-complex app that can load and unload view controllers, this happens all the time.

Mark Bessey
Yeah but what's happening is my objects are getting re-built and i'm getting two of each. I could but checks in to see if they're != nil but I haven't had to do this in previous apps. Something weird is going on here. Any ideas what might be calling it irregularly so I look deeper for the cause?
Meltemi
In the second case, it's unarchiving an NSArray, which apparently has a reference to the RootViewController. Do you maybe have more than one reference to a RootViewController in the same nib file?
Mark Bessey
I agree, check to see if you have defined 2 instances of RootViewController
Panagiotis Korros
+2  A: 

You can't assume viewDidLoad will be called only once. If you are initializing objects and want a guarantee do the initialization either in the init method or if you are loading from a nib file from the awakeFromNib method.

Greg Martin
+2  A: 

I did some debugging and here's what I found about the ViewController loading order:

initWithNibName:bundle:     self = <original instance>, retainedOutlet = 0x0  
loadView >>>                self = <original instance>, retainedOutlet = 0x0  
      initWithCoder:        self = <coder instance>,    retainedOutlet = 0x0  
      initWithCoder:        self = <coder instance>,    retainedOutlet = 0x0  
      setView:              self = <original instance>, retainedOutlet = 0x0  
      setRetainedOutlet:    self = <original instance>, retainedOutlet = 0x1613c40  
      viewDidLoad           self = <coder instance>,    retainedOutlet = 0x0  
      awakeFromNib          self = <coder instance>,    retainedOutlet = 0x0  
loadView <<<  
viewDidLoad                 self = <original instance>, retainedOutlet = 0x1613c40  
viewWillAppear:             self = <original instance>, retainedOutlet = 0x1613c40  
dealloc                     self = <coder instance>,    retainedOutlet = 0x0
viewDidAppear:              self = <original instance>, retainedOutlet = 0x1613c40

During the loadView method, initWithCoder: is called and a new copy of the viewController is created. this is what is passed into a few of the methods (like viewDidLoad). the copy is destroyed later in a dealloc call. the good news is that in this copy, retained outlets are not configured, so you can use this as a test to know if you should initialize variables, call other methods, and most importantly, if you should release and destroy objects during dealloc.

key takeaway: the real viewController will have its retained IBOutlet properties configured. if you are in an overridden method that is getting called multiple times, just check one of your retained IBOutlet properties for NULL. if they are NULL, then return immediately.

anybody got any clues as to why this is happening this way?

side effect of this: you can't use awakeFromNib reliably.

HTH

FingerTipFun
A: 

I had a similar problem and it was a result of renaming my XIB file and its ViewController class (File Owner). Don't do that -- as it really got the views and delegates misdefined inside the XML and it was not recoverable. Meanwhile, I had a reference to the load the original VC that was supposed to be my new VC. I believe that caused the parent to recreate itself and then the VC I was really tried to invoke. Basically, I created an indirect recursion to the VC that has x2 viewDidLoad entries in my trace.

I don't think there is any valid reason for x2 viewDidLoad as it is a genesis and can invoke other initialization with the wrong assumed pre-conditions. Every time I have seen the x2 viewDidLoad, it was a coding error on my part -- quite often when I was refactoring and moving VC classes around.

If there is a valid reason for more than on viewDidLoad call, please someone (Apple Dev are you listening) explain it in technical detail -- I have been searching for that answer for months now.

mobibob