简体   繁体   English

为什么UIViewController子类的Swift默认初始值设定项初始化属性两次?

[英]Why does the Swift default initializer of a UIViewController subclass initialize the properties twice?

Why does the Swift default initializer init() of a UIViewController subclass initialize the properties twice? 为什么UIViewController子类的Swift默认初始化程序init()初始化属性两次? The same thing happens with subclasses of UIView, but not with a direct subclass of NSObject. UIView的子类也会发生同样的事情,但NSObject的子类却没有。

The problem goes away by using Parent(nibName: nil, bundle: nil) instead of Parent() for initialization. 通过使用Parent(nibName: nil, bundle: nil)而不是Parent()进行初始化,问题就消失了。 It also works correctly when I provide custom initializers for Parent . 当我为Parent提供自定义初始化程序时,它也可以正常工作。

I know how to work around this problem, but I am curious about why it happens. 我知道如何解决这个问题,但我很好奇它为什么会发生。

The problem can be reproduced by copying this code into an Xcode 6.0.1 Playground. 通过将此代码复制到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)



Update: When I define a fake class that has similar initializers like the ones UIViewController has and use that as the superclass of Parent both ways to initialize it work and print "Child init" only once. 更新:当我定义一个假类,它具有类似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)


  • Is UIViewController's convenience init() supposed to work that way? UIViewController的便利性init()应该以这种方式工作吗?
  • Is there a bug in the implementation of UIViewController's convenience init()? 在UIViewController的方便init()的实现中是否存在错误?
  • Is init() a valid initializer for UIViewController? init()是UIViewController的有效初始值设定项吗? Maybe it's not and let parent = Parent() in the first example should not even compile? 也许它不是, let parent = Parent()第一个例子中的let parent = Parent()甚至不应该编译?

The first print occurs when constructing the Parent instance; 构造Parent实例时发生第一次打印; all of the instance fields are initialized at that point, which includes creating the Child instance. 此时初始化所有实例字段,包括创建Child实例。

The second print occurs when the implicit super.init is called of the Parent. 当调用Parent的隐式super.init时,会发生第二次打印。 Given that the code for this is closed, it's impossible to know for sure what is happening; 鉴于此代码已关闭,无法确切知道发生了什么; but the problem probably stems from the fact that the init is a convenience initializer in UIViewcontroller (the designated initializer is init:nibName:bundle ). 但问题可能源于这样一个事实,即initUIViewcontroller一个便利初始化UIViewcontroller (指定的初始化器是init:nibName:bundle )。 Documentation in the UIVIewController states that when it is overridden, the required initializer must be called. UIVIewController中的文档声明,当它被覆盖时,必须调用所需的初始化程序。

So to correct this, you need to add: 所以要纠正这个问题,你需要添加:

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)
  }
}

See https://developer.apple.com/library/prerelease/iOS/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_319 for more information about designated vs convenience initializers. 有关指定与便利初始化程序的详细信息,请参阅https://developer.apple.com/library/prerelease/iOS/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_319

Apparently, UIViewContoller 's init is implemented like: 显然, UIViewContollerinit实现如下:

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

You can see, with debugger, that self of Parent has different address between first Child() call and second one. 您可以使用调试器看到Parent self在第一个Child()调用和第二个调用之间具有不同的地址。

In Swift, properties are initialised before the owner object is initialised. 在Swift中,在初始化所有者对象之前初始化属性。 that is why your Child() is get called twice. 这就是你的Child()被调用两次的原因。

This problem has been fixed in Xcode 6.3. Xcode 6.3中已修复此问题。 The last version where I can reproduce the bug is Xcode 6.2. 我可以重现该错误的最后一个版本是Xcode 6.2。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 用 Swift 初始化 UIViewController 的子类? - Initialize a subclass of UIViewController with Swift? UIViewController如何设法在Swift中使用默认的no-args初始化程序? - How does UIViewController manage to have a default no-args initializer in Swift? 如何子类化UIViewController并快速添加属性? - How to subclass a UIViewController and add properties in swift? Swift - 初始化UIViewController - Swift - initialize UIViewController 为什么UIViewController是UIResponder的子类? - Why UIViewController is a subclass of UIResponder? 为什么UIPopoverController不是UIViewController子类? - Why is UIPopoverController not a UIViewController subclass? 为什么我必须继续为我的编程 UIViewController 子类声明相同的必需但未实现的初始化程序(编码器 aDecoder)? - Why must I keep declaring the same required but not implemented initializer for init(coder aDecoder) for my programatic UIViewController subclass? Swift-初始化UIView的子类 - Swift - Initialize a subclass of UIView 如何在保留数据的同时将UIViewController下转换为子类的子类呢? - In swift how does one downcast a UIViewController to subclass of a subclass while retaining data? UIViewController子类无法识别UITapGesture - UIViewController Subclass does not recognize UITapGesture
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM