【问题标题】:Why does the Swift default initializer of a UIViewController subclass initialize the properties twice?为什么 UIViewController 子类的 Swift 默认初始化程序会两次初始化属性?
【发布时间】:2014-11-22 22:36:21
【问题描述】:

为什么 UIViewController 子类的 Swift 默认初始化器init() 会两次初始化属性? UIView 的子类也会发生同样的事情,但 NSObject 的直接子类不会。

使用Parent(nibName: nil, bundle: nil) 而不是Parent() 进行初始化,问题就消失了。当我为Parent 提供自定义初始化程序时,它也可以正常工作。

我知道如何解决这个问题,但我很好奇它为什么会发生。

可以通过将此代码复制到 Xcode 6.0.1 Playground 来重现该问题。

import UIKit

class Child {
    init() {
        println("Child init")
    }
}

class Parent: UIViewController {
    let child = Child()
}

// This way "Child init" is printed twice:
let parent = Parent()

// This way "Child init" is printed once:
//let parent = Parent(nibName: nil, bundle: nil)



更新:当我定义一个具有类似初始化器的假类时,UIViewController 拥有类似的初始化器,并将其用作Parent 的超类,两种方式都可以初始化它只打印一次“Child init”。

import UIKit

class Child {
    init() {
        println("Child init")
    }
}

class FakeViewController : UIResponder {
    init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {

    }

    convenience override init() {
        self.init(nibName: nil, bundle: nil)
    }
}

class Parent: FakeViewController {
    let child = Child()
}

// With the FakeViewController both initializers cause "Child init" to be printed once:
let parent = Parent()
//let parent = Parent(nibName: nil, bundle: nil)


  • UIViewController 的便利 init() 应该这样工作吗?
  • UIViewController 的便捷init() 的实现是否有bug?
  • init() 是 UIViewController 的有效初始化程序吗?也许不是,第一个示例中的 let parent = Parent() 甚至不应该编译?

【问题讨论】:

  • 查看 UIViewController 中的文档,以及子类化时需要提供的内容。通过插入 FakeViewController,您可以使用以下建议来解决问题。

标签: ios swift initialization xcode6


【解决方案1】:

此问题已在 Xcode 6.3 中修复。我可以重现该错误的最后一个版本是 Xcode 6.2。

【讨论】:

    【解决方案2】:

    显然,UIViewContollerinit 是这样实现的:

    - (instancetype)init {
        self = [super init]; // <- not sure
        if(self) {
            self = [[self.class alloc] initWithNibName:nil bundle:nil];
        }
        return self;
    }
    

    您可以使用调试器看到Parentself 在第一个Child() 调用和第二个调用之间具有不同的地址。

    在 Swift 中,属性被初始化所有者对象被初始化之前。这就是为什么你的Child() 会被调用两次。

    【讨论】:

      【解决方案3】:

      第一次打印发生在构造 Parent 实例时;所有实例字段都在此时初始化,包括创建 Child 实例。

      第二次打印发生在调用 Parent 的隐式 super.init 时。鉴于此代码已关闭,因此无法确定发生了什么;但问题可能源于initUIViewcontroller 中的便利初始化程序(指定的初始化程序是init:nibName:bundle)。 UIVIewController 中的文档指出,当它被覆盖时,必须调用所需的初始化程序。

      所以要纠正这个问题,你需要添加:

      class Parent: UIViewController {
        override init() {
          super.init(nibName:nil,bundle:nil)
        }
        // the following is also required if implementing an initializer
        required init(coder:NSCoder) {
          super.init(coder:coder)
        }
      }
      

      请参阅https://developer.apple.com/library/prerelease/iOS/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_319 了解有关指定与便利初始化程序的更多信息。

      【讨论】:

      • 我在 "override init() {" 行收到错误 "Initializer does not override a specified initializer from its superclass"
      猜你喜欢
      • 1970-01-01
      • 2015-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-15
      • 2019-06-16
      • 1970-01-01
      相关资源
      最近更新 更多