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

发布于 2024-07-26 07:00:44 字数 2188 浏览 9 评论 0原文

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

秋千易 2024-08-02 07:00:44

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

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

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

I had this same issue when my app was first launching. What I found was that in my MainWindow.xib file, I was setting both my App Delegate's viewController outlet, and my Window's rootViewController outlet to my root view controller. When you build a View Based project file in Xcode, your App Delegate's didFinishLaunchingWithOptions will be pre-populated with:

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

I believe that the self.viewController ivar is instantiated from MainWindow.xib before didFinishLaunchingWithOptions gets called. Then the pre-populated code above sets the Window's rootViewController. So if, in conjunction, you specify the rootViewController outlet for the Window in your MainWindow.xib file, your root view controller will actually be created twice and added as the Window's root view controller two times.

分分钟 2024-08-02 07:00:44

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

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

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

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

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.

夜吻♂芭芘 2024-08-02 07:00:44

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

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

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.

你是暖光i 2024-08-02 07:00:44

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

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.

铃予 2024-08-02 07:00:44

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

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

如果有正当理由超过 viewDidLoad 调用,请有人(Apple Dev 你在听吗)详细解释一下技术细节 - 我几个月来一直在寻找这个答案。

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.

等风来 2024-08-02 07:00:44

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

解决方案

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

详细信息

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

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

I had this problem but was able to fix it.

Solution:

Rename the view controller class that is loading twice.

Details:

Rename it and make the new name something entirely new. Renaming the file doesn't stop the load-twice issue. Creating a new project (as suggested by others) might be overkill, at least try the simpler solutions first! Rename the class of the destination VC.

Hint:
If renaming the class fixes your issue, you then obviously have to update all your references to that class. You can speed this up by using Command+Shift+F for project-wide find.

新人笑 2024-08-02 07:00:44

当我从头开始重新设计 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 的 view 属性的文档,就会明白:

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

I ran into the same problem as I was redesigning a ViewController from scratch to get rid of the XIB file and to make the class reusable. I had this second ViewController instance which would receive a viewDidLoad message followed by a dealloc message.

I found out this was the result of the loadView method not being redefined in the ViewController. The default loadView called awakeFromNib, with the nibName property set to the class name. Even though I had removed the XIB file from the project, it was still in the application directory on the simulator.

So even though you could just reset the contents and settings of the simulator to get rid of the second viewDidLoad, a better way may be to just redefine loadView like this:

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

It makes sense if you consider the documentation for the UIViewController's view property:

If you access this property and its
value is currently nil, the view
controller automatically calls the
loadView method and returns the
resulting view. The default loadView
method attempts to load the view from
the nib file associated with the view
controller (if any). If your view
controller does not have an associated
nib file, you should override the
loadView method and use it to create
the root view and all of its subviews.

蘑菇王子 2024-08-02 07:00:44

中分配了两次 rootViewController

就我而言,我没有注意到我实际上在application:didFinishLaunchingWithOptions:applicationDidBecomeActive:

In my case, I didn't notice that I have actually assigned the rootViewController twice in:

application:didFinishLaunchingWithOptions: and applicationDidBecomeActive:

爱给你人给你 2024-08-02 07:00:44

除此之外,如果您使用的是系统功能,例如 TouchID,那么您的 AppDelegate 中的 applicationWillResignActive 将被调用,如果您说,将控制器重置为保护根控制器,然后您将被重新调用,并且 performSegueWithIdentifier(self.MAIN_SEGUE ,sender: self) 将不会触发!

Just to add to this, if you are using a system function, such as TouchID, then applicationWillResignActive in your AppDelegate will get invoked and if you are say, resetting controllers to a Secure Root Controller then you get reinvoked, and performSegueWithIdentifier(self.MAIN_SEGUE ,sender: self) will not fire!

南风起 2024-08-02 07:00:44

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

这怎么导致我的视​​图控制器加载两次? 将代码从 Storyboard 项目传输到 xib 项目时,我制作了几个 xib(每个 ViewController 一个)并从 Storyboard 中复制 viewcontroller 对象。 这导致 xib 中没有视图,而是视图控制器; 这意味着我已将视图控制器放入视图控制器中(因为文件的所有者也是视图控制器的实例)。 我不认为您遇到这个问题,但我希望有一天它可能对某人有所帮助。

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

This happened to me when I merged a project from the storyboard to the old way using xibs for constructing views. The main reason for switching back was the fact I couldn't properly put up a modal view properly. The way I usually do it is by having a delegate method from a UIButton construct an instance of a certain viewcontroller, set some of its properties (the most import one being the delegate so i can properly dismiss the modal view controller again) and then present it in a modal way. In the new storyboard way, this is supposedly done with a segue. Customizing the transition is only doable by making a custom class that extends the UIStoryboardSegue class. I find this way too much hassle compared to the simple way it used to be so I merged back.

How did this cause me to have a viewcontroller load twice? When transferring the code from the storyboard project to the xib project, I made a couple of xibs (one for each ViewController) and copied the viewcontroller object from the storyboard. This led to a xib with in it not a viw, but a viewcontroller; meaning i had put a viewcontroller in a viewcontroller (since the file's owner is also an instance of the viewcontroller). I don't think in your case that you had this problem but I hope it maybe helps someone some day.

To fix this move the view from the view controller out of the view controller and to the root level of the objects section. Both the view controller and it's navigation item should be deleted. Build and run and you should see only one allocation for the view controller. This is the file owner.

铁憨憨 2024-08-02 07:00:44

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

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

如果某些属性用 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;
  }
}

What if your code did access the view property when it is not loaded yet, the view controller will create just empty view and it could trigger view did load accidentally.

Most common error is accessing view property during initialization. May be some property accessor(setter) that is invoked by xib should access view property accidentally.

What if some property is annotated with IBInspectable you should have to check isViewLoaded before apply some value to view.


-(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;
  }
}

絕版丫頭 2024-08-02 07:00:44

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

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

I did make something like this in SceneDelegate to check something and forget removing it.
Then I find out that ends up calling viewDidLoad again.

func sceneDidBecomeActive(_ scene: UIScene) {
    ViewController().view.frame.origin.x = 100
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文