【问题标题】:viewDidLoad getting called twice on rootViewController at launchviewDidLoad 在启动时在 rootViewController 上被调用两次
【发布时间】:2010-11-08 01:02:56
【问题描述】:

有人知道为什么这个根 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

【问题讨论】:

    标签: iphone cocoa-touch uiviewcontroller viewdidload


    【解决方案1】:

    我的应用首次启动时遇到了同样的问题。我发现在我的 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 的根视图控制器添加两次。

    【讨论】:

    • 这正是我遇到的问题。
    • 同样的问题。那是一个病态的交易。谢谢哈里森利。
    • 所有那个随机粗体使这真的很难阅读。
    • 刚刚将粗体改为pre
    • 在我的例子中,VC的init方法被调用了一次,但是viewDidLoad被调用了两次
    【解决方案2】:

    我做了一些调试,下面是我发现的关于 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 调用中被销毁。好消息是,在这个副本中,保留的 outlet 没有配置,所以你可以用它来测试你是否应该初始化变量,调用其他方法,最重要的是,你是否应该在 dealloc 期间释放和销毁对象。

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

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

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 是的,但是正在发生的事情是我的对象正在重新构建,并且我得到了两个。我可以检查它们是否为 != nil 但我不必在以前的应用程序中这样做。这里发生了一些奇怪的事情。有什么想法可能会不规则地调用它,以便我更深入地寻找原因?
      • 在第二种情况下,它正在取消归档一个 NSArray,它显然具有对 RootViewController 的引用。您是否可能在同一个 nib 文件中对 RootViewController 有多个引用?
      • 我同意,检查你是否定义了 2 个 RootViewController 实例
      • 虽然 Mark 基本正确,但 viewDidUnload 和 viewDidLoad 是对应的方法。至少应该可以假设对于每个 viewDidLoad 调用都会有另一个 viewDidUnload 调用。这样你就可以对 viewDidLoad 中的设置进行拆解。当后者在没有 viewDidUnload 的情况下按顺序被多次调用时,事情可能会出错。这根本不应该发生。
      • 这是不正确的。 viewDidUnload 仅在内存不足的情况下调用。不保证每次调用 viewDidLoad 都会调用它。
      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

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

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

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

        【讨论】:

        • 重命名项目中的一些文件后出现同样的问题。最终创建了一个新项目。
        • @Bemmu 我认为这也是我的问题。我有 99.9% 的把握,对于我看到的双重负载,我没有两个 segue 或任何其他解释。这是我记得在其中玩弄名称并可能删除/重新创建文件的一个视图控制器。有没有办法绕过创建一个全新的项目?将代码复制到新文件中是可以的,但我在故事板文件中完成了很多工作,并且不愿意重新创建每个 VC 的设计/布局约束等。
        【解决方案6】:

        我遇到了这个问题,但能够解决它。

        解决方案:

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

        详情:

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

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

        【讨论】:

        • 我的一个观点也有类似的问题。重命名视图控制器本身不起作用。我不得不删除并重新创建视图的 segue。这一定是 XCode 的错误,因为我之前没有重命名或更改故事板。
        • 真的很奇怪,谢谢你。如果您看到这条消息 DaveG 或其他人 - 它是 第一个 还是 第二个 是真实的?就我而言,它是第一个。我想知道这是否是同一个问题集。
        【解决方案7】:

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

        我发现这是loadView 方法没有在ViewController 中重新定义的结果。默认的loadView 称为awakeFromNib,nibName 属性设置为类名。即使我从项目中删除了 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 方法并使用它来创建 根视图及其所有子视图。

        【讨论】:

          【解决方案8】:

          在我的例子中,我没有注意到我实际上已经两次分配了 rootViewController:

          application:didFinishLaunchingWithOptions: 和 applicationDidBecomeActive:

          【讨论】:

            【解决方案9】:

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

            【讨论】:

            • 是的,使用 TouchID 会导致调用 applicationWillResignActive 并再次激活。
            【解决方案10】:

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

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

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

            【讨论】:

              【解决方案11】:

              如果您的代码在尚未加载时确实访问了视图属性,视图控制器将只创建空视图,并且可能会意外触发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;
                }
              }
              
              

              【讨论】:

                【解决方案12】:

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

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-12-24
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-07-24
                  • 1970-01-01
                  相关资源
                  最近更新 更多