[class] Swift 이니셜 라이저가 수퍼 클래스에서 편의 이니셜 라이저를 호출 할 수없는 이유는 무엇입니까?

두 가지 클래스를 고려하십시오.

class A {
    var x: Int

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

    convenience init() {
        self.init(x: 0)
    }
}

class B: A {
    init() {
        super.init() // Error: Must call a designated initializer of the superclass 'A'
    }
}

왜 이것이 허용되지 않는지 모르겠습니다. 궁극적으로 각 클래스의 지정된 이니셜 라이저는 필요한 값으로 호출됩니다. 그래서 의 편의 가 잘 작동 할 때 기본값을 다시 지정하여 B‘s 에서 자신을 반복해야하는 이유는 무엇입니까?initxinitA



답변

이것은 Swift Programming Guide에 명시된 “Initializer Chaining”규칙의 규칙 1입니다.

규칙 1 : 지정된 이니셜 라이저는 직계 수퍼 클래스에서 지정된 이니셜 라이저를 호출해야합니다 .

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html

내 강조. 지정된 이니셜 라이저는 편의 이니셜 라이저를 호출 할 수 없습니다.

이니셜 라이저 “방향”이 허용되는 규칙을 보여주는 다이어그램이 있습니다.

이니셜 라이저 체인


답변

중히 여기다

class A
{
    var a: Int
    var b: Int

    init (a: Int, b: Int) {
        print("Entering A.init(a,b)")
        self.a = a; self.b = b
    }

    convenience init(a: Int) {
        print("Entering A.init(a)")
        self.init(a: a, b: 0)
    }

    convenience init() {
        print("Entering A.init()")
        self.init(a:0)
    }
}


class B : A
{
    var c: Int

    override init(a: Int, b: Int)
    {
        print("Entering B.init(a,b)")
        self.c = 0; super.init(a: a, b: b)
    }
}

var b = B()

클래스 A의 모든 지정된 이니셜 라이저가 재정의되기 때문에 클래스 B는 A의 모든 편의 이니셜 라이저를 상속합니다. 따라서이를 실행하면

Entering A.init()
Entering A.init(a:)
Entering B.init(a:,b:)
Entering A.init(a:,b:)

이제 지정된 이니셜 라이저 B.init (a : b :)가 기본 클래스 편의 이니셜 라이저 A.init (a :)를 호출하도록 허용하면 B.init (a :, b :)에 대한 재귀 호출이 발생합니다. ).


답변

무한 재귀로 끝날 수 있기 때문입니다. 중히 여기다:

class SuperClass {
    init() {
    }

    convenience init(value: Int) {
        // calls init() of the current class
        // so init() for SubClass if the instance
        // is a SubClass
        self.init()
    }
}

class SubClass : SuperClass {
    override init() {
        super.init(value: 10)
    }
}

그리고보세요 :

let a = SubClass()

어느 전화 SubClass.init()가 전화 SuperClass.init(value:)할 것 SubClass.init()입니다.

지정 / 편의 초기화 규칙은 클래스 초기화가 항상 정확하도록 설계되었습니다.


답변

이에 대한 해결 방법을 찾았습니다. 매우 예쁘지는 않지만 수퍼 클래스의 값을 모르거나 기본값을 설정하려는 문제를 해결합니다.

당신이해야 할 일은 init바로 init서브 클래스 에서 편리함을 사용하여 슈퍼 클래스의 인스턴스를 만드는 것입니다 . 그런 다음 init방금 만든 인스턴스를 사용하여 지정된 수퍼를 호출합니다 .

class A {
    var x: Int

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

    convenience init() {
        self.init(x: 0)
    }
}

class B: A {
    init() {
        // calls A's convenience init, gets instance of A with default x value
        let intermediate = A()

        super.init(x: intermediate.x)
    }
}


답변

편리한 초기화 코드를 init()새로운 도우미 함수로 추출하고 하위 클래스에서 초기화를 수행하도록 foo()호출 foo(...)하십시오.


답변

이니셜 라이저와 그 상속에 대한 자세한 설명은 18:30에 WWDC 비디오 “403 intermediate Swift”를 참조하십시오. 내가 이해했듯이 다음을 고려하십시오.

class Dragon {
    var legs: Int
    var isFlying: Bool

    init(legs: Int, isFlying: Bool) {
        self.legs = legs
        self.isFlying = isFlying
    }

    convenience initWyvern() {
        self.init(legs: 2, isFlying: true)
    }
}

하지만 이제 Wyrm 하위 직업을 생각해보십시오. Wyrm은 다리도 날개도없는 용입니다. 따라서 와이번의 이니셜 라이저 (2 개 다리, 2 개 날개)가 잘못되었습니다! 편리한 Wyvern-Initializer를 단순히 호출 할 수없고 지정된 전체 초기화 프로그램 만 호출하면이 오류를 피할 수 있습니다.

class Wyrm: Dragon {
    init() {
        super.init(legs: 0, isFlying: false)
    }
}


답변

왜 당신은 두 개의 이니셜 라이저를 가지고 있지 않습니까? 하나는 기본값을 가지고 있습니까?

class A {
  var x: Int

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

  init() {
    self.x = 0
  }
}

class B: A {
  override init() {
    super.init()

    // Do something else
  }
}

let s = B()
s.x // 0