대체 왜 super.init전에 저장속성을 초기화해야하는거지?라는 생각이들어서 찾아보다가 알게된 내용들을 정리해보려한다
클래스의 상속에서 initalize를 공부할때 가장 중요했던 부분이
1. 저장속성을 초기화 한다
2. super class의 initalize를 호출한다
3. 1번과 2번의 순서를 "꼭"지켜야한다
이거였다
사실그때는 이게 swift의 규칙이구나 하고 넘어갔던 기억이났는데 왜그런가 갑자기 생각해보다보니 좀 공부를 하게된거같다
class Human {
var name: String
init(name: String) {
self.name = name
print(name)
}
}
class Student: Human {
var language: String
init(language: String) {
self.language = language
super.init(name: "youth")
print(language)
}
}
class Me: Student {
var nickName: String
init(nickName: String) {
self.nickName = nickName
super.init(language: "leaderKim")
print(nickName)
}
}
Human이라는 class가있고 이 class를 Student가 상속하고 Student를 Me class가 상속한다고 생각해보자
Swift클래스에는 2단계 초기화 Rule이 있다
1. 1단계는 클래스에 정의한 각각의 저장 프로퍼티에 초깃값이 할당된다.
2. 모든 저장 프로퍼티의 초기 상태가 결정되면 2단계로 돌입해 저장 프로퍼티들을 사용자정의할 기회를 얻는다.
당연히 처음에는 뭔말인지 몰랐다
하나하나 보면 1단계같은경우는 우리가 알고있는 그대로다 해당클래스의 저장속성이 있다면 저장속성을 초기화하고 super class의 이니셜라이즈를 실행시켜서 상속한 저장속성들을 초기화시켠다는것이다.
딱 한마디로 말하면 1단계는 "모든 프로퍼티는 초기값을 가져야한다"인데 "모든"은 현재 클래스의 저장속성과 super class를 상속했다면 당연히 super class의 속성도 가지고있기때문에 이것도 초기값을 가지게 해야한다는 거다.
근데 이것만 보면 "근데 꼭 내걸먼저 초기화 시키고 super init할필요는 없는거아닌가?"라고 생각할수도있는데 각각의 단계에 4가지 safety check를 하게되는데 여기에 관련한 rule이 있다.
우선은 여기서 중요한건 "모든 프로퍼티에 초기값을 할당하는것"을 기억하면된다.
그리고 모든 프로퍼티에 초기값을 할당하는 1단계가 끝나야 인스턴스가 생성된다.
이 뜻은 뭐냐 현재클래스에 저장속성을 초기화 하고 super.init하기전에 print로 현재클래스의 저장속성을 찍어보면 안찍힌다는 뜻이다
이유는 생각해보면 간단하다 super.init을 안했으니까 "모든"저장속성이 초기화가 안된거고 그렇다는말은 인스턴스자체가 생성이 안된거다 당연히 print를 찍으면 에러가 발생한다
2단계는 1단계를 완료했을경우인데 이때 저장속성의 값을 새로정의할수있다는 뜻이다
이것도 조금만 생각해보면 1단계를 완료하기전에 저장속성의 값을 새로정의하게되면 super.init을 할때 super class의 초기화에 의해 내가 설정한 값이 덮어질 수 있기때문에 super.init 이후에(super.init이후라는건 1단계가 끝나고 인스턴스가 만들어진 후) 프로퍼티에 접근해서 새 값을 할당해줄 수 있다.
그리고 당연히 클래스의 메서드도 1단계 초기화가 끝난후에 호출할 수 있다
4가지 safety check
스위프트 컴파일러는 2단계 초기화를 오류 없이 처리하기 위해 다음과 같은 네 가지 안전확인(Safty-checks)을 실행한다.
안전 확인 1단계 :
지정 초기자는 클래스 안에서 초기화를 superclass의 초기자에게 위임하기 전에 모든 프로퍼티를 초기화 해야 합니다. 위에서 언급한 것 처럼 메모리에서 객체는 모든 저장된 프로퍼티가 초기 상태를 갖어야만 완전히 초기화 된것으로 간주되기 때문에 이 규칙을 만족시키기 위해 지정 초기자는 반드시 다른 초기자로 넘기기 전에 소유하고있는 모든 프로퍼티를 초기화 해야 합니다.
안전 확인 2단계 :
지정 초기자는 반드시 상속된 값을 할당하기 전에 superclass의 초기자로 위임을 넘겨야 합니다. 그렇지 않으면 상속된 값이 superclass이 초기자에 의해 덮어 쓰여지게 됩니다.
안전 확인 3단계 :
편리한 초기자는 반드시 어떤 프로퍼티를 할당하기 전에 다른 초기자로 위임을 넘겨야 합니다. 만약 그렇지 않으면 편리한 초기자에 의해 할당된 값을 다른 클래스의 지정 초기자에 의해 덮어 쓰여지게 됩니다.
안전 확인 4단계 :
이니셜라이저는 초기화의 1단계가 끝나기 전에는 self의 값을 참조하거나 어떤 인스턴스 프로퍼티, 메소드 등을 호출하거나 읽을 수 없습니다.
1단계 부터 보면
이건 간단하게 말해서 rule이다 해당 class의 저장속성을 초기화한다음에 super.init을 호출할것
2단계를 보면
사실 2단계가 무슨말인가 했더니 super.init을 선언하기전에는 super class의 속성이 초기화가 되지 않은상태이므로 해당속성에 접근하거나 값을 할당할수없다는 뜻이다
3단계를 보면
편의생성자에 관한 내용인데 편의생성자는 무조건 해당 class의 지정생성자를 실행해야한다. 근데 해당 class의 init이 나중에 먼저 불려야 인스턴스를 생성하고 어떤 프로퍼티를 할당할수있다. 프로퍼티 먼저할당하고 init을 하게되면 당연히 init에 의해 값이 덮어씌워진다
4단게를 보면 뭐 이건 당연한건데 2단계 초기화에서 1단계가 끝나기전에는 self로 값을 참조하거나 함수를 호출할수없다는거다 인스턴스가 생성이 안된거니까 당연하다