왜 Swift가 서브 클래스의 적절한 필드를 먼저 초기화합니까?


9

Swift 언어에서 인스턴스를 초기화하려면 해당 클래스의 모든 필드를 채우고 superconstructor를 호출해야합니다.

class Base {
    var name: String

    init(name: String) {
        self.name = name
    }
}

class Derived: Base {
    var number: Int

    init(name: String, number: Int) {
        // won't compile if interchange lines
        self.number = number
        super.init(name)
    }
}

필자의 경우 self필드에 값을 할당하기 전에 인스턴스 를 만들어야하고 코드 할당 후에 만 ​​체인이 연결되는 것처럼 인상 을 주기 때문에 역순으로 보입니다 . 그 외에도 수퍼 클래스는 서브 클래스의 도입 된 속성을 읽을 수있는 법적 수단이 없으므로이 경우 안전은 중요하지 않습니다.

또한 JavaScript와 같은 많은 다른 언어와 Swift의 다소 영적인 조상 인 Objective C조차도 액세스하기 전에 체인 호출을 요구합니다 self.

슈퍼 컨스트럭터를 호출하기 전에 필드를 정의해야하는이 선택의 이유는 무엇입니까?


흥미 롭군 연쇄 후와 같이 메소드 호출 (특히 가상)을 배치 할 수있는 제한이 있습니까? C #에서는 서브 클래스의 필드에 기본값이 주어진 다음 체인 / 슈퍼 클래스 구성을 통해 실제 IIRC로 초기화 할 수 있습니다.
Erik Eidt

3
요점은에 대한 무제한 액세스를 허용하기 전에 모든 필드를 초기화해야한다는 것 self입니다.
코드 InChaos

@ErikEidt : Swift에서는 선택적 필드 만 자동으로 nil로 초기화됩니다.
gnasher729

답변:


9

C ++에서 파생 개체를 만들면 기본 생성자가 실행되는 동안 기본 개체로 시작되므로 기본 생성자가 실행될 때 파생 멤버도 존재하지 않습니다. 따라서 초기화 할 필요가 없으며 초기화 할 수 없습니다. Base 생성자가 완료된 경우에만 초기화되지 않은 필드가 많은 파생 오브젝트로 오브젝트가 변경됩니다.

Swift에서 Derived 객체를 만들면 처음부터 Derived 객체입니다. 메소드가 대체되는 경우 Base init 메소드는 이미 대체 된 메소드를 사용하므로 파생 멤버 변수에 액세스 할 수 있습니다. 따라서 Base init 메소드를 호출하기 전에 모든 파생 멤버 변수 초기화 해야합니다 .

추신. Objective-C를 언급했습니다. Objective-C에서는 모든 것이 자동으로 0 / nil / NO로 초기화됩니다. 그러나 해당 값이 변수를 초기화하기위한 올바른 값이 아닌 경우 Base init 메소드는 대체 된 메소드를 쉽게 호출 할 수 있으며 아직 초기화되지 않은 변수를 올바른 값 대신 0의 값으로 사용합니다. Objective-C에서는 언어 규칙을 위반하는 것이 아니라 (코드가 작동하도록 정의 된 방식 임) 분명히 코드의 버그입니다. Swift에서 해당 버그는 언어에 의해 허용되지 않습니다.

추신. "처음부터 파생 된 오브젝트입니까, 아니면 언어 규칙으로 인해 관찰 할 수없는"주석이 있습니까? Derived 클래스는 Base init 메소드가 호출되기 전에 자체 멤버를 초기화했으며이 파생 멤버는 값을 유지합니다. 그러니 그것 입니다 자료 초기화가 호출 될 때 파생 된 객체, 또는 컴파일러는 오히려 기괴한 일을해야합니다. 그리고 Base init 메소드가 모든 Base 인스턴스 멤버를 초기화 한 직후 재정의 된 함수를 호출 할 수 있으며 이는 파생 클래스의 인스턴스임을 증명합니다.


매우 합리적입니다. 나는 예제를 자유롭게 추가했다. 내가 불분명하거나 그 점을 잘못 이해했다면 자유롭게 롤백하십시오.
Zomagk

처음부터 파생 된 객체입니까, 아니면 언어 규칙으로 인해이를 관찰 할 수 없습니까? 나는 그것이 후자라고 생각합니다.
중복 제거기

9

이는 언어 문서의 초기화 페이지 에서 2 단계 초기화 섹션에 설명 된 Swift의 안전 규칙에서 비롯된 것입니다 .

사용하기 전에 모든 필드가 설정되도록합니다 (충돌을 피하기 위해 포인터).

Swift는 2 단계 초기화 시퀀스로이를 달성합니다. 각 이니셜 라이저는 모든 인스턴스 필드를 초기화 한 다음 수퍼 클래스 이니셜 라이저를 호출하여 마찬가지로 수행해야합니다. 그런 다음 트리에서 발생하는 경우에만이 이니셜 라이저가 self포인터를 이스케이프 할 수 있도록 허용 합니다. 메소드 또는 인스턴스 특성 값을 읽습니다.

그런 다음 개체가 올바르게 구성되었는지 확인하여 추가 초기화를 수행 할 수 있습니다. 특히, 모든 비 선택적 포인터는 유효한 값을 갖습니다. nil은 유효하지 않습니다.

목표 C는 0 또는 nil이 항상 유효한 값이라는 점을 제외하고 크게 다르지 않으므로 첫 번째 단계 초기화는 모든 필드를 0으로 설정하는 할당 자에 의해 수행됩니다. 또한 Swift에는 변경할 수없는 필드가 있으므로 1 단계에서 초기화해야합니다. . 그리고 스위프트는 이러한 안전 규칙을 시행합니다.


물론 MI에서는 훨씬 어려울 것입니다.
중복 제거기

3

치다

  • 기본 클래스에 정의 된 가상 메소드는 파생 클래스에서 다시 정의 할 수 있습니다.
  • 기본 클래스의 계약자는이 가상 메소드를 직접 또는 간접적으로 호출 할 수 있습니다.
  • 재정의 (파생 클래스) 가상 메소드는 유도 수준 계약에 정확하게 유도 클래스 세트 인 필드의 값에 의존 할 수있다.
  • 파생 클래스의 계약자는 기본 클래스 계약자에 설정된 필드에 따라 기본 클래스의 메서드를 호출 할 수 있습니다.

따라서 가상 방법이 허용 될 때 계약 업체를 안전하게 만드는 단순한 설계가 없으므로 Swift는 2 단계 초기화를 요구하여 이러한 문제를 방지하므로 프로그래머에게 더 나은 안전성을 제공하면서도보다 복잡한 언어를 만들 수 있습니다.

좋은 방법으로 이러한 문제를 해결할 수 있다면 "이동"을 통과하지 말고 PHd 수집을 직접 진행하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.