21

任何人都知道为什么这个根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
4

12 回答 12

16

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

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

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

于 2011-09-01T19:47:32.280 回答
8

我做了一些调试,这是我发现的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 期间释放和销毁对象。

关键要点:真实viewControllerIBOutlet配置其保留的属性。如果您在一个被多次调用的覆盖方法中,只需检查您的保留IBOutlet属性之一NULL。如果是NULL,则立即返回。

有人知道为什么会这样吗?

这样做的副作用:你不能awakeFromNib可靠地使用。

于 2010-02-05T17:44:53.130 回答
4

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

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

于 2009-07-03T23:41:03.387 回答
3

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

于 2009-07-04T01:02:09.870 回答
3

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

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

如果有正当理由不viewDidLoad随叫随到,请某人(Apple Dev 你在听)详细解释一下技术细节——我几个月来一直在寻找那个答案。

于 2010-02-19T19:27:04.273 回答
3

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

解决方案

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

详情

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

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

于 2015-09-05T07:52:20.090 回答
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如果您考虑view 属性的文档,这是有道理的:

如果您访问此属性并且其值当前为 nil,则视图控制器会自动调用该 loadView方法并返回结果视图。默认loadView 方法尝试从与视图控制器(如果有)关联的 nib 文件加载视图。如果您的视图控制器没有关联的 nib 文件,您应该重写该 loadView方法并使用它来创建根视图及其所有子视图。

于 2011-02-09T15:10:38.447 回答
1

就我而言,我没有注意到我实际上已经两次分配了 rootViewController:

application:didFinishLaunchingWithOptions:applicationDidBecomeActive:

于 2014-03-18T03:11:38.520 回答
1

再补充一点,如果您使用的是系统功能,例如 TouchID,则AppDelegate 中的applicationWillResignActive将被调用,如果您说,将控制器重置为安全根控制器,那么您将被重新调用,并执行SegueWithIdentifier(self.MAIN_SEGUE ,sender: self)不会触发!

于 2016-02-18T07:43:58.480 回答
0

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

这如何导致我两次加载视图控制器?在将代码从情节提要项目传输到 xib 项目时,我制作了几个 xib(每个 ViewController 一个)并从情节提要中复制了 viewcontroller 对象。这导致了一个 xib,其中不是一个视图,而是一个视图控制器;意思是我在视图控制器中放置了一个视图控制器(因为文件的所有者也是视图控制器的一个实例)。在您的情况下,我认为您没有遇到此问题,但我希望有一天它可能对某人有所帮助。

要解决此问题,请将视图从视图控制器移出视图控制器并移动到对象部分的根级别。视图控制器和它的导航项都应该被删除。构建并运行,您应该只看到视图控制器的一个分配。这是文件所有者。

于 2011-11-21T08:15:06.150 回答
0

如果您的代码在尚未加载时确实访问了视图属性,那么视图控制器将只创建空视图,并且可能会view did load意外触发。

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

如果某些属性被注释了,IBInspectable你应该isViewLoaded在应用一些值之前检查一下。


-(void) setSomeProperty:(UIColor*) someColor
{
  _someColor = someColor;
  if(self.isViewLoaded) {
    // self.view causes view creation and invokes 'viewDidLoad' then the view is not ready yet.
    self.view.backgroundColor = someColor;
  }
}

-(void) viewDidLoad
{
  [super viewDidLoad]
  if(_someColor){
    self.view.backgroundColor = _someColor;
  }
}

于 2019-08-06T00:52:49.647 回答
0

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

func sceneDidBecomeActive(_ scene: UIScene) {
    ViewController().view.frame.origin.x = 100
}
于 2020-09-17T07:07:00.993 回答