[properties] Swift 클래스 오류 : super.init 호출에서 속성이 초기화되지 않았습니다

나는 두 개의 수업이 Shape있고Square

class Shape {
    var numberOfSides = 0
    var name: String
    init(name:String) {
        self.name = name
    }
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

class Square: Shape {
    var sideLength: Double

    init(sideLength:Double, name:String) {
        super.init(name:name) // Error here
        self.sideLength = sideLength
        numberOfSides = 4
    }
    func area () -> Double {
        return sideLength * sideLength
    }
}

위의 구현으로 오류가 발생합니다.

property 'self.sideLength' not initialized at super.init call
    super.init(name:name)

self.sideLength전화하기 전에 왜 설정 해야 super.init합니까?



답변

귀하의 질문에 대답하는 Swift Programming Language에서 인용하십시오 :

“Swift의 컴파일러는 4 단계의 유용한 안전 점검을 수행하여 오류없이 2 단계 초기화가 완료되었는지 확인합니다.”

안전 점검 1 “지정된 이니셜 라이저는 클래스에 의해 도입 된 모든 속성이 수퍼 클래스 이니셜 라이저로 위임되기 전에 초기화되어야합니다.”

발췌 : Apple Inc.“Swift Programming Language.” iBooks.
https://itunes.apple.com/us/book/swift-programming-language/id881256329?mt=11


답변

Swift에는 초기화 프로그램에서 수행되는 매우 명확하고 구체적인 작업 순서가 있습니다. 몇 가지 기본 예제로 시작하여 일반적인 경우까지 진행해 보겠습니다.

객체 A를 봅시다. 다음과 같이 정의하겠습니다.

class A {
    var x: Int
    init(x: Int) {
        self.x = x
    }
}

A에는 수퍼 클래스가 없으므로 super.init () 함수가 존재하지 않으므로이를 호출 할 수 없습니다.

자 이제 B라는 새로운 클래스로 A를 서브 클래스하자.

class B: A {
    var y: Int
    init(x: Int, y: Int) {
        self.y = y
        super.init(x: x)
    }
}

이것은 Objective-C에서 출발하여 [super init]일반적으로 다른 것보다 먼저 호출됩니다. 스위프트에서는 그렇지 않습니다. 메소드 호출 (수퍼 클래스의 이니셜 라이저 포함)을 포함하여 다른 작업을 수행하기 전에 인스턴스 변수가 일관된 상태인지 확인해야합니다.


답변

로부터 문서

안전 점검 1

지정된 이니셜 라이저는 클래스가 도입 한 모든 속성이 수퍼 클래스 이니셜 라이저로 위임되기 전에 초기화되어야합니다.


왜 이런 안전 점검이 필요한가요?

이에 대한 답을 얻으려면 초기화 과정을 신속하게 진행하십시오.

2 단계 초기화

Swift의 클래스 초기화는 2 단계 프로세스입니다. 첫 번째 단계에서는 저장된 각 속성에 속성을 도입 한 클래스에 의해 초기 값이 할당됩니다. 모든 저장된 속성의 초기 상태가 결정되면 두 번째 단계가 시작되고 새 클래스를 사용할 준비가 된 것으로 간주되기 전에 각 클래스에 저장된 속성을 사용자 지정할 수있는 기회가 주어집니다.

2 단계 초기화 프로세스를 사용하면 클래스 계층 구조의 각 클래스에 완전한 유연성을 제공하면서 초기화가 안전 해집니다. 2 단계 초기화는 속성 값이 초기화되기 전에 속성 값에 액세스 하지 못하게하고 다른 초기화 프로그램이 속성 값을 예기치 않게 다른 값으로 설정하지 못하게합니다.

따라서 2 단계 초기화 프로세스가 위에 정의 된대로 수행되도록하기 위해 4 가지 안전 점검이 있습니다.

안전 점검 1

지정된 이니셜 라이저는 클래스가 도입 한 모든 속성이 수퍼 클래스 이니셜 라이저로 위임되기 전에 초기화되어야합니다.

이제 2 단계 초기화는 순서에 대해 이야기하지 않지만이 안전 검사 super.init는 모든 속성을 초기화 한 후에 순서를 지정합니다.

안전 점검 1은 2 단계 초기화로 인해이 안전 점검 1없이 속성 값을 초기화하기 전에 액세스 할 수 없도록 하기 때문에 관련이없는 것처럼 보일
수 있습니다.

이 샘플 에서처럼

class Shape {
    var name: String
    var sides : Int
    init(sides:Int, named: String) {
        self.sides = sides
        self.name = named
    }
}

class Triangle: Shape {
    var hypotenuse: Int
    init(hypotenuse:Int) {
        super.init(sides: 3, named: "Triangle")
        self.hypotenuse = hypotenuse
    }
}

Triangle.init사용하기 전에 모든 속성을 초기화했습니다. 안전 점검 1은 관련이없는 것 같습니다.

하지만 약간 복잡한 시나리오가있을 수 있습니다.

class Shape {
    var name: String
    var sides : Int
    init(sides:Int, named: String) {
        self.sides = sides
        self.name = named
        printShapeDescription()
    }
    func printShapeDescription() {
        print("Shape Name :\(self.name)")
        print("Sides :\(self.sides)")
    }
}

class Triangle: Shape {
    var hypotenuse: Int
    init(hypotenuse:Int) {
        self.hypotenuse = hypotenuse
        super.init(sides: 3, named: "Triangle")
    }

    override func printShapeDescription() {
        super.printShapeDescription()
        print("Hypotenuse :\(self.hypotenuse)")
    }
}

let triangle = Triangle(hypotenuse: 12)

출력 :

Shape Name :Triangle
Sides :3
Hypotenuse :12

여기에서 super.init를 설정하기 전에를 호출했다면 호출은를 호출했을 것이고 hypotenuse, 그 이후에는 우선의 삼각형 클래스 구현으로 대체 될 것입니다 . 삼각형 클래스 액세스는 비 선택적 특성 아직 초기화되지 않았습니다. 그리고 2 단계 초기화로 인해 속성 값이 초기화되기 전에 액세스 할 수 없으므로 허용되지 않습니다.super.initprintShapeDescription()printShapeDescription()printShapeDescription()hypotenuse

따라서 2 단계 초기화가 정의 된대로 수행되고, 특정 호출 순서 가 있어야하며, 즉 클래스에 super.init의해 도입 된 모든 속성을 초기화 한 후 안전 점검이self 필요합니다.


답변

모든 인스턴스 변수를 초기화 한 후 “super.init ()”를 호출해야합니다.

약 28:40에 Apple의 “Intermediate Swift”비디오 (Apple 개발자 비디오 리소스 페이지 https://developer.apple.com/videos/wwdc/2014/ 에서 찾을 수 있음 )에서 모든 초기화 프로그램이 인스턴스 변수를 초기화 한 후 수퍼 클래스를 호출해야합니다.

Objective-C에서는 그 반대였습니다. Swift에서는 모든 속성을 사용하기 전에 초기화해야하므로 먼저 속성을 초기화해야합니다. 이는 속성을 먼저 초기화하지 않고 수퍼 클래스의 “init ()”메서드에서 재정의 된 함수를 호출하지 못하도록하기위한 것입니다.

따라서 “Square”의 구현은 다음과 같아야합니다.

class Square: Shape {
    var sideLength: Double

    init(sideLength:Double, name:String) {
        self.sideLength = sideLength
        numberOfSides = 4
        super.init(name:name) // Correct position for "super.init()"
    }
    func area () -> Double {
        return sideLength * sideLength
    }
}


답변

못생긴 형식으로 죄송합니다. 선언 후에 질문 문자를 넣으면 모든 것이 정상입니다. 질문은 컴파일러에게 값이 선택적이라는 것을 알려줍니다.

class Square: Shape {
    var sideLength: Double?   // <=== like this ..

    init(sideLength:Double, name:String) {
        super.init(name:name) // Error here
        self.sideLength = sideLength
        numberOfSides = 4
    }
    func area () -> Double {
        return sideLength * sideLength
    }
}

편집 1 :

이 오류를 건너 뛰는 더 좋은 방법이 있습니다. jmaschad의 의견에 따르면 귀하의 경우 선택 사항을 사용해야 할 이유가 없습니다. 따라서 선언 후 멤버를 초기화하면됩니다.

class Square: Shape {
    var sideLength: Double=Double()

    init(sideLength:Double, name:String) {
        super.init(name:name)
        self.sideLength = sideLength
        numberOfSides = 4
    }
    func area () -> Double {
        return sideLength * sideLength
    }
}

편집 2 :

이 대답에 두 가지 마이너스를 얻은 후에 더 나은 방법을 찾았습니다. 생성자에서 클래스 멤버를 초기화하려면 생성자 내부와 super.init () 호출 전에 초기 값을 지정해야합니다. 이처럼 :

class Square: Shape {
    var sideLength: Double

    init(sideLength:Double, name:String) {
        self.sideLength = sideLength   // <= before super.init call..
        super.init(name:name)
        numberOfSides = 4
    }
    func area () -> Double {
        return sideLength * sideLength
    }
}

Swift를 배우는 행운을 빕니다.


답변

swift는 모든 멤버를 사용하기 전에 var를 초기화하도록합니다. 슈퍼 턴일 때 어떤 일이 발생하는지 확신 할 수 없기 때문에 오류가 발생합니다. 죄송합니다.


답변

에드워드,

예제에서 다음과 같이 코드를 수정할 수 있습니다.

var playerShip:PlayerShip!
var deltaPoint = CGPointZero

init(size: CGSize)
{
    super.init(size: size)
    playerLayerNode.addChild(playerShip)
}

이것은 암시 적으로 랩핑되지 않은 옵션을 사용하고 있습니다.

문서에서 우리는 읽을 수 있습니다 :

“선택적 옵션과 마찬가지로 암시 적으로 래핑되지 않은 선택적 변수 또는 속성을 선언 할 때 초기 값을 제공하지 않으면 자동으로 값이 nil로 설정됩니다.”