viewDidLoad在整个init方法执行之前被调用。

14

编辑:这里是完整的代码示例,适用于Xcode 6.4

我有一个简单的iOS应用程序,没有故事板。 我像这样在AppDelegate.swift中为UIWindow设置了rootViewController:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    let tabBarController = TabBarController()

    window = UIWindow(frame: UIScreen.mainScreen().bounds)
    window?.rootViewController = tabBarController
    window?.makeKeyAndVisible()

    return true
}

TabBarController类的实现如下:

class TabBarController: UITabBarController {

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)

        // Next line is called after 'viewDidLoad' method
        println("init(nibName: bundle:)")
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        println("viewDidLoad")
    }

}

当我运行应用程序时,控制台输出如下:

viewDidLoad
init(nibName: bundle:)

这意味着在 super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 之后的代码将在 viewDidLoad 方法之后被调用! 这仅适用于从UITabBarController继承的类。 如果您尝试使用UIViewController作为基类,一切都很好,并且在执行init方法之后将调用viewDidLoad

3个回答

16
您不能保证 viewDidLoad 仅在 init 方法完成后被调用。当视图控制器需要加载其视图层次结构时,会调用 viewDidLoad
在内部,TabBarController 的 init 方法(通过调用 super.init)正在执行某些导致视图加载的操作。
这适用于所有的视图控制器。例如:如果您创建一个 UIViewController 子类并在 init 上对其 view 属性进行任何处理,比如添加子视图,甚至只是设置视图的 backgroundColor 属性 - 您会注意到相同的行为。

2
谢谢。但我认为这是不一致的行为,而且文档也没有很好地记录下来。我已经向苹果提交了TSI,并且很想看到他们的回答。 - Deny
@Deny 我很想听听苹果公司在回答你时有什么话要说,但我怀疑你不会得到太多额外的信息。这是标准的iOS行为。不过你关于TabBarController子类化缺乏文档的观点是正确的。 - Artal
我想知道为什么你会在 init 中对 UIViewController 的视图进行任何操作? - Petar

13

来源: http://www.andrewmonshizadeh.com/2015/02/23/uitabbarcontroller-is-different/

这可能不会让人感到惊讶,但显然UITabBarController与大多数视图控制器的行为不同。生命周期在它和其他视图控制器之间可能是“相同”的,但它执行操作的顺序不同。

也就是说,当你创建一个UITabBarController的子类并提供自己的自定义初始化程序时,你会注意到viewDidLoad方法被调用的方式有些出乎意料。也就是说,当你调用[super init](或UITabBarController上的其他初始化程序)时,它将在初始化过程中调用loadView,这将导致调用你的viewDidLoad。这可能不是你所期望的,因为大多数(所有?)其他UIViewController子类不会在初始化过程中实例化它们的视图。因此,如果你提供了一个自定义的初始化程序,并期望在加载视图之前进行一些设置,然后在加载视图后添加你的包含的视图控制器,这将破坏你的逻辑。

解决方案是将你的设置代码实际上从标准的viewDidLoad方法移出,放到一个特殊的设置方法中,在你自定义初始化程序的末尾调用它。这让我想到了“代码异味”,苹果不应该允许这种情况发生。不过,很可能这是因为UITabBarController需要将UITabBar添加到UIViewController的视图中,这就要求视图存在。这就是loadView被触发的原因。


3
似乎UITabBarController的init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?)函数会出于某种原因调用viewDidLoad。如果在print("viewDidLoad")行上设置断点,您将看到该调用作为初始化序列的一部分进行。如果您将视图控制器更改为子类化UIViewController,则会发现viewDidLoad不是作为初始化序列的一部分被调用的,而是由于调用makeKeyAndVisible而调用的。
我不知道苹果为什么这样编码,但我怀疑这是为了给选项卡栏控制器提供在加载内容视图控制器之前设置事物的机会。
无论如何,如果您想要子类化UITabBarController,这只是您必须处理的事情。

你说得对。我也得出了同样的结论。即便如此,我还是很想知道苹果工程师的评论。我会在这里发布它的。 - Deny

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接