Swiftのdesignated/convenience/required/default initializerと継承

(2020-06-23)

Initialization — The Swift Programming Language (Swift 5.3)

designated initializerはプライマリなinitializerで、全ての初期化されていないプロパティを初期化し、スーパークラスのinit()を呼んでチェーンを作る。 convenience initializerは利便性のためのinitializerで、他のinitializerを呼んで最終的にdesignated initializerが呼ばれるようにする。 required initializerは継承が必須なinitializerで、サブクラスにも存在することが保証される。

class A: NSObject {
    var some: String

    // designated initializer
    override init() {
        print("designated")
        some = "foo"
    }

    // designated & required initializer
    required init(_ str: String) {
        print("required \(str)")
        some = "bar"
    }
    
    convenience init(_ num: Int) {
        self.init()
        print("convenience \(num)")
    }
    
    convenience init(num2: Int) {
        self.init(num2)
        print("convenience2 \(num2)")
    }
}

designated initializerはクラスに一つ以上存在する必要があるが、次のように全てのプロパティが初期化されている場合default initializerが作られる。

class D {
    var some: String = "A"
    
    convenience init(_ str: String) {
        self.init()
        some = str
    }
}

デフォルトでinitializerは継承されないが、 全てのdesignated initializerが定義されていない場合はまとめて継承される。 全てのdesignated initializerが継承されている場合は全てのconvenience initializerも継承される。

class B: A {
    required init(_ str: String) {
        super.init(str)
        print("overridden required \(str)")
        some = "bar"
    }
    
    convenience init(num2: Int) {
        // self.init() error
        self.init("a")
        print("overridden convenience2 \(num2)")
    }
}

class C: A {
    convenience init(num2: Int) {
        self.init()
        print("overridden convenience2 \(num2)")
    }
}

B(1) // error
C(1)

例えばrequired initializerを持つUIViewで他のdesignated initializerを定義するとまとめて継承されなくなるので明示的に継承しなくてはいけない。

class C: UIView {
    // 'required' initializer 'init(coder:)' must be provided by subclass of 'UIView
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
}