viewDidLoad在启动时在rootViewController上被调用两次

时间:2009-07-03 23:31:35

标签: iphone cocoa-touch uiviewcontroller viewdidload

有人知道为什么这个根View Controller's viewDidLoad在发布时被调用了两次?这让我疯了!

这是从第一次到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

第二次:

#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

12 个答案:

答案 0 :(得分:15)

我的应用首次启动时遇到了同样的问题。我发现在我的MainWindow.xib文件中,我将我的App Delegate的viewController插座和我的Window的rootViewController插座设置为我的根视图控制器。在Xcode中构建基于视图的项目文件时,您的App Delegate的didFinishLaunchingWithOptions将预先填充:

self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;

我相信self.viewController ivar会在调用didFinishLaunchingWithOptions之前从MainWindow.xib实例化。然后上面预先填充的代码设置Window的rootViewController。因此,如果您在MainWindow.xib文件中为Window指定rootViewController出口,则实际上将创建两次根视图控制器并将其作为Window的根视图控制器添加两次。

答案 1 :(得分:7)

我做了一些调试,这是我发现的ViewController加载顺序:

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

在loadView方法期间,调用initWithCoder:并创建viewController的新副本。这是传递给一些方法的内容(如viewDidLoad)。稍后在dealloc调用中销毁副本。好消息是,在此副本中,未配置保留的出口,因此您可以将其用作测试,以了解是否应该初始化变量,调用其他方法,最重要的是,如果您应该在dealloc期间释放和销毁对象。 / p>

关键点:真正的viewController将保留其保留的IBOutlet属性。如果您处于被多次调用的重写方法中,只需选中IBOutlet之一的已保留NULL属性即可。如果它们是NULL,则立即返回。

有人知道为什么会这样发生这种情况吗?

这样的副作用:您无法可靠地使用awakeFromNib

答案 2 :(得分:4)

怪异。我没有看到这种特殊情况,但一般来说,你应该假设viewDidLoad可以被多次调用。每当引用该控制器的nib文件被加载时,它就会被调用。

对于只有一个笔尖的简单应用程序,这不应该发生。但是在一个可以加载和卸载视图控制器的更复杂的应用程序中,这种情况一直都会发生。

答案 3 :(得分:3)

您不能假设viewDidLoad只会被调用一次。如果要初始化对象并希望保证在init方法中执行初始化,或者从awakeFromNib方法从nib文件加载。

答案 4 :(得分:3)

我遇到了类似的问题,这是重命名我的XIB文件及其ViewController类(文件所有者)的结果。不要这样做 - 因为它确实在XML中错误定义了视图和委托,并且它是不可恢复的。与此同时,我参考了原本应该是我的新VC的VC。我相信这导致父母重新创建自己然后VC我真的试图调用。基本上,我创建了一个间接递归VC,在我的跟踪中有x2 viewDidLoad个条目。

我认为x2 viewDidLoad没有任何正当理由,因为它是一个起源,并且可以使用错误的假设前置条件调用其他初始化。每次我看到x2 viewDidLoad时,我都会遇到编码错误 - 通常是在重构和移动VC类时。

如果有一个有效的理由超过viewDidLoad来电,请某人(Apple Dev你在听)以技术细节解释它 - 我几个月来一直在寻找这个答案。

答案 5 :(得分:3)

我有这个问题,但能够解决它。

<强>解决方案

重命名加载两次的视图控制器类。

<强>详情:

重命名它并使新名称成为全新的名称。 重命名文件不会停止加载两次问题。创建一个新项目(正如其他人所建议的)可能有点矫枉过正,至少首先尝试更简单的解决方案!重命名目标VC的类。

<强>提示: 如果重命名该类可以修复您的问题,那么您显然必须更新对该类的所有引用。您可以通过使用Command + Shift + F进行项目范围的查找来加快速度。

答案 6 :(得分:2)

我遇到了同样的问题,因为我从头开始重新设计ViewController以摆脱XIB文件并使类可重用。我有第二个ViewController实例,它会收到一条viewDidLoad消息,然后是一个dealloc消息。

我发现这是loadView方法未在ViewController中重新定义的结果。默认loadView名为awakeFromNibnibName属性设置为类名。即使我从项目中删除了XIB文件,它仍然在模拟器上的应用程序目录中。

因此即使您可以重置模拟器的内容和设置以摆脱第二个viewDidLoad,更好的方法是重新定义loadView,如下所示:

- (void)loadView {
    self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease];
    self.view.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin; 
}

如果您考虑UIViewController's视图属性的文档:

,这是有意义的
  

如果您访问此属性及其属性   价值目前是零,视图   控制器自动调用   loadView方法并返回   结果观点。默认loadView   方法尝试从中加载视图   与视图关联的nib文件   控制器(如果有的话)。如果你的观点   控制器没有关联   nib文件,你应该覆盖   loadView方法并使用它来创建   根视图及其所有子视图。

答案 7 :(得分:1)

在我的情况下,我没有注意到我实际上已经两次分配了rootViewController:

application:didFinishLaunchingWithOptions:applicationDidBecomeActive:

答案 8 :(得分:1)

只是为了补充一点,如果您使用的是系统功能,例如TouchID,则会调用AppDelegate中的 applicationWillResignActive ,如果您这样做,则重置控制器到安全根控制器然后你被重新调用,并且 performSegueWithIdentifier(self.MAIN_SEGUE,sender:self) 将不会被激活!

答案 9 :(得分:0)

当我将故事板中的项目与使用xib构建视图的旧方法合并时,这种情况发生在我身上。转回的主要原因是我无法正确地提出模态视图。我通常这样做的方法是通过从UIButton构造一个委托方法构建某个viewcontroller的实例,设置它的一些属性(最重要的一个是委托,所以我可以再次正确地解除模态视图控制器)然后呈现它以模态的方式。在新的故事板方式中,这应该是用segue完成的。只能通过创建扩展UIStoryboardSegue类的自定义类来自定义转换。与过去简单的方式相比,我发现这种方式太麻烦了,所以我合并了。

这是怎么导致我有两次viewcontroller加载?将代码从storyboard项目传输到xib项目时,我为故事板创建了几个xib(每个ViewController一个)并且复制了 viewcontroller对象。这导致了一个xib,它不是一个viw,而是一个viewcontroller;意思是我把一个viewcontroller放在一个viewcontroller中(因为该文件的所有者也是viewcontroller的一个实例)。在你的情况下我不认为你有这个问题,但我希望有一天它可能对某人有帮助。

要修复此操作,请将视图控制器中的视图移出视图控制器并移动到对象部分的根级别。应删除视图控制器及其导航项。构建并运行,您应该只看到一个视图控制器的分配。这是文件所有者。

答案 10 :(得分:0)

如果您的代码尚未加载时访问了view属性,该视图控制器将只创建一个空视图,并且可能会意外触发MapJsonKey

最常见的错误是初始化期间访问视图属性。可能是xib调用的某些属性访问器(设置程序)应意外访问view属性。

如果某些属性用view did load注释怎么办,则应先检查IBInspectable,然后再应用一些值进行查看。

isViewLoaded

答案 11 :(得分:0)

我确实在SceneDelegate中做了类似的事情来检查并忘记删除它。 然后我发现最终又再次调用了viewDidLoad。

func sceneDidBecomeActive(_ scene: UIScene) {
    ViewController().view.frame.origin.x = 100
}