-
Swift - Initializer - Two Phase InitializationProgramming/Swift 2021. 12. 6. 16:02
안녕하세요 BeePeach입니다 :)
오늘은 Init이 어떻게 초기화되는지에 대해서 조금 더 자세히 공부해보려고 합니다.
처음 보면 어렵게 느껴지지만 사실 어려운 게 하나도 없답니다 :)
천천히 여러 번 읽으면 충분이 이해하실 수 있습니다!!
Swift 컴파일러의 4가지 안정성 체크
Swift에서 class의 초기화는 총 2단계로 진행됩니다.
첫 번째 단계에서는 class에서 정의한 모든 stored property에 초기값이 할당됩니다.
모든 stored property의 초기값이 정의되었다면 두 번째 단계가 시작됩니다.
두 번째 단계에서는 인스턴스를 사용할 준비가 되기 전에 stored property들을 custom 할 수 있게 됩니다.
Swift complier는 2단계 초기화가 에러 없이 성공하기 위해서 4가지의 안정성 체크(Safety check)를 합니다.
- Designated init은 super.init을 호출하기 전에 상속받지 않은 자신의 모든 프로퍼티들이 초기화되어있는지 확인합니다.
- Designated init은 상속받은 프로퍼티에 값을 할당하기 전에 super.init()을 호출해야 합니다. 만약 super.init()을 호출하기 전에 값을 할당했다면 superclass에서 초기화한 값으로 값이 재정의 됩니다.
- Convenience init은 모든 프로퍼티들에 값을 할당하기 전에 self.init()을 호출해야합니다. 만약 프로퍼티에 값을 할당하고 self.init()을 호출한다면 designated init에서 초기화한 값으로 할당한 값이 재정의 됩니다.
- Initializer는 1번째 단계가 초기화되기전까지 모든 인스턴스 프로퍼티 값을 읽을 수 없고, 모든 인스턴스 메서드를 호출할 수 없으며 self 프로퍼티를 이용한 문법을 사용할 수 없습니다.
Two Phase Initialization (2단계 초기화)
위에서 초기화는 총 2단계라고 설명했습니다.
그럼 각 단계가 어떻게 진행되는지 자세히 보도록 하겠습니다.
Phase 1
- Designated 또는 Convenience init이 호출됩니다.
- 새로운 인스턴스를 위한 메모리 공간이 할당됩니다. 이때는 아직 초기화가 이루어지지 않았습니다.
- Designated init는 class에 선언된 모든 stored property가 초기값을 가지고 있는지 확인합니다. 확인이 완료되면 현재 class의 stored property를 위한 메모리는 모두 초기화가 완료되었습니다. 하지만 아직 상위 클래스의 메모리는 초기화되지 않았습니다.
- Designated init은 3번과 같은 작업을 하도록 superclass의 init에게 초기화 과정을 위임합니다. 초기화 과정을 위임받은 Superclass의 designated init은 똑같이 현재 class의 stored property를 위한 메모리 초기화를 완료합니다.
- 이 작업은 상속 체인을 따라서 최상위 class가 모두 초기화될 때까지 계속됩니다.
- 이 작업이 완료되면 최초로 init을 호출한 class는 모든 프로퍼티가 값을 가지고 있는 것을 보장받습니다. 그럼 인스턴스의 메모리가 초기화되었고 Phase 1이 종료됩니다.
Phase 2
- 최상위 class부터 상속 체인을 따라 아래로 내려가면서 designated init은 인스턴스를 custom 할 기회를 가집니다. 이때부터 self 프로퍼티를 사용할 수 있으며 instance Method를 호출할 수 있고 프로퍼티의 값을 수정할 수 있습니다.
- 마지막으로 모든 convenience init가 인스턴스를 custom 할 기회를 가지게 됩니다. convenience init은 이때부터 self 프로퍼티를 사용할 수 있습니다.
Example
그럼 이제까지 공부한 개념을 이용해서 간단한 init을 생성해보도록 하겠습니다.
이 코드는 Figure라는 superClass를 선언하고 subclass인 Rectangle을 선언했습니다.
그리고 각 stored property에 초기값이 없으므로 생성자로 초기화를 시켜줘야겠죠??
그래서 init(width:height:name:)을 선언하고 초기화를 시켰습니다.
하지만 이 코드는 잘못된 코드입니다!
이전 포스팅에서 공부했던 Initializer Delegatation을 다시 생각해보면 Designated initializer는 반드시 superclass의 designated init을 호출해야 합니다.
name 프로퍼티를 상속받았으니 self.name = name으로 초기화시키는 게 아니라 super.init을 호출하여 초기화시켜야 합니다.
그럼 다시 코드를 고쳐볼까요??
자 시킨 대로 super.init(name:)을 이용해서 초기화하도록 했습니다.
그럼 Initializer Delegation의 조건도 만족시키니까 초기화에 성공했겠죠???
아쉽지만 아닙니다.. 실행해보면 에러가 발생합니다.
위에서 1번 안정성 체크를 다시 한번 확인해주세요.
컴파일러는 super.init()을 호출하기 전에 상속받지 않은 자신의 모든 프로퍼티를 초기화하는지 체크합니다.
우리는 Rectangle의 프로퍼티를 초기화하기 전에 super.init(name:)을 호출하고 있죠?? 그럼 여기서 에러가 발생합니다.
super.init(name:)의 위치를 변경했을 뿐인데 에러가 사라졌습니다.
현재 class의 모든 stored property가 값을 가지고 있는지 (초기화되어있는지) 먼저 확인한 후에 super.init(name:)을 통해서 superclass의 stored property를 초기화시키기 때문입니다.
먼저 super.init(name:)을 호출하면 아직 현재 class의 stored property들은 값이 초기화되지 않았기 때문에 컴파일러는 이것을 확인하고 안정성 체크 1에 위배되기 때문에 에러가 발생하는 것입니다.
나머지 안정성 체크는 딱히 사용하는 코드가 없기 때문에 모두 만족하게 됩니다.
이후에 코드를 추가하여 모두 확인해 볼 수 있도록 하겠습니다 :)
마무리
2단계 초기화의 과정을 이해하고 컴파일러의 안정성 체크를 다시 읽어보면 이제 모든 게 이해가 갑니다.
첫 번째는 superclass의 init에게 초기화를 위임하기 전에 현재 class의 stored property의 모든 값이 초기화되어있는지 확인하기 때문입니다.
두 번째는 superclass의 init에게 초기화를 위임한 뒤 superclass의 stored property를 위한 메모리 할당이 끝나기 때문이겠죠??
세 번째는 Convenience Init을 모든 designated init이 완료된 후에 실행되기 때문에 self.init()을 먼저 호출해야 합니다.
네 번째는 Phase 1이 끝나기 전에는 프로퍼티를 위한 메모리가 할당이 되기 전이므로 프로퍼티 값에 접근할 수 없고 인스턴스를 위한 메모리가 모두 초기화되지 않았으니 메서드 호출과 self 프로퍼티를 사용할 수 없는 것입니다.
참고자료
https://docs.swift.org/swift-book/LanguageGuide/Initialization.html
728x90'Programming > Swift' 카테고리의 다른 글
Swift - Failable InitialIzer (0) 2021.12.17 Swift - Initializer - Inheritance, Overriding (0) 2021.12.11 Swift - Initializer Delegation (0) 2021.11.28 Swift - Initializer 기초 (1) 2021.11.13 Swift - Inheritance (상속) (0) 2021.11.11