12

Consider two classes. The first is Vehicle, an NSObject subclass that conforms to NSCopying:

class Vehicle : NSObject, NSCopying {

    var wheels = 4

    func copyWithZone(zone: NSZone) -> AnyObject {
        let vehicle = self.dynamicType()
        vehicle.wheels = self.wheels
        return vehicle
    }
}

The second class, Starship, inherits from Vehicle:

class Starship : Vehicle {

    var photonTorpedos = 6
    var antiGravity = true

    override func copyWithZone(zone: NSZone) -> AnyObject {
        let starship = super.copyWithZone(zone) as Starship

        starship.photonTorpedos = self.photonTorpedos
        starship.antiGravity = self.antiGravity
        return starship
    }
}

This code doesn't compile because:

Constructing an object of class type 'Vehicle' with a metatype value must use a 'required' initializer.

So I go ahead and add a required initializer:

required override init () {
    super.init()
}

And now the app compiles, and Starship objects respond to copy() properly.

Two questions:

  1. Why does constructing an object with a metatype need a required initializer? (It appears the initializer I wrote does nothing.)
  2. Is there anything I wrote incorrectly, or should add to the initializer? Is there a case I'm not considering?
Aaron Brager
  • 65,323
  • 19
  • 161
  • 287

2 Answers2

11

The Short Answer

You cannot use self.dynamicType() without marking init() as required because there's no guarantee subclasses of Vehicle will also implement init().

Exploring The Problem

Taking a look at The Swift Programming Language: Initialization, it's mentioned how

subclasses do not inherit their superclass initializers by default

The situations in which a subclass will inherit its superclass' initialisers are:

Assuming that you provide default values for any new properties you introduce in a subclass, the following two rules apply:

Rule 1

If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.

Rule 2

If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initialisers.

Take a look at the example:

class MySuperclass {
    let num = 0

    // MySuperclass is given `init()` as its default initialiser
    // because I gave `num` a default value.
}

class MySubclass : MySuperclass {
    let otherNum: Int

    init(otherNum: Int) {
        self.otherNum = otherNum
    }
}  

According to the information above, since MySubclass defined the property otherNum without an initial value, it doesn't automatically inherit init() from MySuperclass.

Now suppose I want to add the following method to MySuperclass:

func myMethod() {
    println(self.dynamicType().num)
}

You'll get the error you described because there is no guarantee subclasses of MySuperclass will implement init() (and in this example they don't).

To solve this problem you therefore need to mark init() as required, to ensure all subclasses of MySuperclass implement init(), and so calling self.dynamicType() is a valid thing to do. It's the same problem as in your question: Swift knows Vehicle implements init(), however it doesn't know any subclasses will implement init() and so you need to make it required.

An alternative solution, which isn't suitable in your example, is to mark Vehicle as final, meaning Vehicle can't be subclassed. Then you'll be able to use self.dynamicType(); but you might as well just use Vehicle() in that case.

ABakerSmith
  • 22,759
  • 9
  • 68
  • 78
0

You need a required initializer because subclass implementation of a required initializer in Swift needs it.

Per Swift Documentation on Required nitializers:

You must also write the required modifier before every subclass implementation of a required initializer, to indicate that the initializer requirement applies to further subclasses in the chain.

Tokuriku
  • 1,332
  • 13
  • 22
  • Actually, it seems to work fine, even if I delete `super.init()`. – Aaron Brager Jan 26 '15 at 04:01
  • Yes, because you override it. I should have said it basically needs an initialiser as it will not necessarily inherit it from it's super class maybe.. – Tokuriku Jan 26 '15 at 04:07
  • That doesn't make sense to me… aren't *all* methods inherited from the super class? Can you say more about this? Is there any documentation you can cite? – Aaron Brager Jan 26 '15 at 04:12