viewDidLoad 方法命名是否具有误导性?

Is the viewDidLoad method naming misleading?

创建 UIViewController 的实例时,只有在需要时才会加载其视图和所有 IBOutlet。 这意味着,与所有其他 类 不同,具有完全初始化的实例并不意味着所有属性都已初始化和加载。

这是一种独特的行为,类似于异步加载,但在绝对完成加载和初始化后没有任何委托或回调方法。

Cocoa(Touch) 中没有其他对象遵循相同的逻辑,即使从 nib 加载 UIView 也是同步的。

因此,可以公平地说 viewDidLoad 方法的命名具有误导性 and/or Apple 其他 API 上下文中的不正确约定?

如果您尝试获取 IBOutlets 进行设置,我认为您可以从中获取正确值的第一个调用是 viewDidLayoutSubviews。这个 get 被调用了很多,所以使用 bool 只调用一次你的设置内容。

视图控制器加载是一种通常称为延迟初始化 的模式。这种模式并不是视图控制器独有的:它出现在 Cocoa 的几个地方,主要与 nib/storyboard-based UI 有关。例如,您放入 nib 中的任何自定义 class(例如 UIView subclass,或 NSView subclass)都不是完全 "ready" 直到它的 awakeFromNib 方法被调用。

当你加载一个 nib 或故事板时(或者当视图控制器系统代表你这样做时),Cocoa(或 Cocoa 触摸)从他们的存档表单初始化 objects在笔尖中,然后遍历每个设置任何 IBOutlet 属性的值。因为那是一个 two-step 过程(而且必须是,因为归档不能以 nib 需要的方式保留到其他 objects 的链接),所以有一个 objects 存在的中间时间(也就是说,他们已经完成了他们的 init 方法和 super.init 链)但是没有完全设置以匹配您在 Interface Builder 中创建的配置。


这里似乎对 "initialized" 的含义有些混淆。 Swift 语言的要求和 ObjC 中的强烈建议是,在初始化程序链完成时,所有属性/实例变量都具有 定义的值 运行.换句话说,如果你在初始化后得到一个 属性 或实例变量的值,通过阅读你的代码你应该知道那个值是什么.

此定义与non-ARC Objective-C的行为形成对比:如果您在初始化期间不分配给实例变量,则它们的值为未定义.它们可以是零,可以是垃圾,可以是其他东西丢弃的内存。

带有 ARC 的 ObjC 部分强制解决了这个问题,确保所有变量都初始化为 zero/nil,即使您在初始化期间没有手动分配给它们。 Swift 通过在编译时要求您在初始化时手动提供值(通过分配给声明它们的属性,通过在 init 定义中分配给它们,或者通过声明它们来进一步强制执行它作为 Optionals 使其默认值为 nil)。


因此,当您处理来自 nib 或故事板的内容时,拥有一个完全初始化的实例意味着所有属性都是 "initialized" 根据语言定义——也就是说,它们的值是已知的。这并不一定意味着它们的值是 "what you want them to be".

特别是,视图控制器旨在用于 delayed-initialization 持续时间超过 "during storyboard loading" 的场景。例如,您可以设置一个完整的视图控制器互连网络(在导航堆栈、选项卡视图、master/detail 拆分视图等中)。最初,这些 VC 实例被初始化并知道它们的一些 non-IBOutlet 属性(例如它们的标题,用于标记标签栏中的非活动标签)。但是昂贵的 UI 基础设施不需要加载,直到系统请求 VC 的视图(通常是在用户即将看到它时)。