Why does the default Swift initializer for a UIViewController subclass initialize properties twice?

Why init()

does Swift's default initializer of a UIViewController subclass initialize properties twice? The same thing happens with subclasses of UIView, but not with a direct subclass of NSObject.

The problem goes away by using Parent(nibName: nil, bundle: nil)

instead Parent()

for initialization. It also works correctly when I provide custom initializers for Parent

.

I know how to get around this problem, but I'm wondering why this is happening.

The issue can be reproduced by copying this code in the 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 one it UIViewController

has and uses it as a superclass from Parent

, both ways to initialize it work and print "child init" only once.

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 it possible that the UIViewController init () function should work this way?
  • Is there a bug in the UIViewController init () convenience implementation?
  • Is init () a valid initializer for a UIViewController? Maybe this is not the case and let parent = Parent()

    in the first example you don't need to compile?
+3


source to share


3 answers


This issue has been fixed in Xcode 6.3. The latest version where I can reproduce the bug is Xcode 6.2.



0


source


The first print occurs when the parent is built; all instance fields are initialized at this point, which includes creating an instance of Child.

The second print is done when the implicit one super.init

is called by the parent. Given that the code is proprietary, it is impossible to know exactly what is going on; but the problem is probably related to what init

is the convenience initializer in UIViewcontroller

(designated initializer init:nibName:bundle

). The documentation in UIVIewController says that when you override it, you must call the required initializer.

So, to fix 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)
  }
}

      

For more information on assignable initializers vs convenience see https://developer.apple.com/library/prerelease/iOS/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097- CH18-XID_319 .

+3


source


Apparently UIViewContoller

init

implemented like this:

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

      

You can see with the debugger that self

of Parent

has a different address between the first call Child()

and the second.

In Swift, properties are initialized before the owner object is initialized. so yours Child()

gets called twice.

+1


source







All Articles