안녕하세요.
이전에 구조체 생성자에 대해 알아봤어요.
오늘은 클래스 생성자를 공부할게요ㅎ
# 1. Designated initializer, Convenience initializer
Designated initializer는 해당 클래스의 모든 property(부모 클래스가 가지고 있는 property 포함)를 초기화하는 생성자입니다.
Convenience initializer는 designated initializer와 유사하지만 생성자 내부에서 designated initializer를 호출할 수 있어요.
클래스는 1개 이상의 designated initializer를 가져야 하며, convenience initializer는 있어도 되고 없어도 됩니다.
# 2. Initializer Delegate for Class Types
'Initializer Delegate'는 인스턴스 초기화를 위해 생성자가 다른 생성자를 호출하는 과정을 말합니다.
designated initializer와 convenience initializer 호출 관계는 아래 3가지 규칙을 따릅니다.
[생성자 호출 규칙]
(1) designated initializer는 직전 superclass의 designated initializer만 호출 가능.
(2) convenience initializer는 동일 클래스 내의 다른 생성자만 호출할 가능.
(3) convenience initializer는 최종적으로 designated initializer를 호출해야 함.
아래 그림을 보면 클래스 계층에서 생성자가 '생성자 호출 규칙'에 따라 어떻게 호출되고 있는지 한눈에 이해가 가실 거예요.
# 3. 2단계 초기화 & 4가지 safety-check
Swift에서 클래스 초기화는 2단계로 진행됩니다.
[2단계 초기화]
(단계 1) 저장 프로퍼티(stored property)에 초기값을 할당
(단계 2) 저장 프로퍼티 값 수정
[참고] 초기화가 2단계로 진행되는 이유?
프로퍼티 값이 초기화되기 전에 프로퍼티 값에 접근하는 것을 방지하고, 다른 생성자에 의해 예상하지 못한 값으로 프로퍼티 값이 설정되는 것을 방지하기 위해.
Swift 컴파일러는 2단계 초기화를 지키기 위해 4가지 유효성 검사(safety-check)를 수행합니다.
[4가지 유효성 검사]
(검사 1) designated initializer는 superclass의 생성자를 호출하기 전에 해당 클래스에서 선언된 모든 프로퍼티 값이 설정되어야 함.
(검사 2) designated initializer는 상속받은 프로퍼티의 값을 할당하기 전에 반드시 superclass 생성자를 호출해야 함.
(검사 3) convenience initializer는 어떤 프로퍼티(상속받은 프로퍼티, 동일 클래스에 선언된 프로퍼티 모두 포함)에 값을 할당하기 전에 반드시 다른 생성자를 호출해야 함.
(검사 4) 초기화 1단계가 완료되기 전까지 생성자는 어떠한 instance method를 호출할 수 없고 프로퍼티 값을 읽을 수 없고 self에 참조할 수 없음.
위 4가지 유효성 검사를 기반으로 2단계 초기화가 어떻게 진행되는지 살펴볼게요.
[초기화 단계 1]
- designated 또는 convenience initializer 호출.
- 인스턴스를 위한 메모리 할당. (메모리는 아직 초기화되지 않음.)
- designated initializer는 해당 클래스에 선언된 모든 stored property 초기화.
- superclass 생성자를 호출하고 클래스 계층을 따라 최상위 클래스까지 동일한 작업 수행.
- 최상위 클래스에 도달하고 모든 stored property에 값이 있다고 보장되면, 인스턴스 메모리는 완전히 초기화된 것으로 간주함.
[초기화 단계 2]
- 최상위 계층에서부터 아래로 내려가면서 각각의 designated initializer는 인스턴스를 커스텀.
- self 접근 가능. 프로퍼티 수정 가능. instance method 호출 가능.
- 마지막으로, convenience initializer는 인스턴스를 커스텀할 수 있고 self에 접근할 수 있음.
# 4. 생성자 상속과 Overriding
Swift의 subclass들은 기본적으로 superclass 생성자를 상속받지 않습니다.
(예외 상황도 있어요! 이건 밑에서 다시 얘기할게요.)
subclass에서 superclass의 designated initializer와 동일한 생성자를 만들고 싶으면 아래처럼 overriding을 해야 합니다.
반대로, subclass에서 superclass의 convenience initializer와 동일한 생성자를 만들고 싶은 경우엔 overriding 하지 않습니다.
[참고] convenience initializer는 왜 overriding 하지 않지?
위에서 설명한 '생성자 호출 규칙'에 의해 superclass의 convenience initializer는 subclass에서 호출할 수 없습니다.
그렇기 때문에 subclass에서 동일한 convenience initializer를 만드는 것은 overriding이 아닙니다.
# 5. 생성자 자동 상속
위에서 설명드렸듯이, 기본적으론 subclass는 superclass 생성자를 상속받지 않아요.
근데, subclass에서 정의된 프로퍼티가 없거나 default 값을 가지는 경우에 한해서 superclass 생성자를 상속받을 수 있는 규칙이 2가지 있어요.
[superclass 생성자를 상속받을 수 있는 2가지 규칙]
(규칙 1) subclass가 어떠한 designated initializer를 정의하지 않은 경우엔 superclass의 designated initializer는 자동으로 상속됨.
(규칙 2) subclass가 superclass의 모든 designated initializer의 구현을 제공한다면, superclass의 convenience initializer는 자동으로 상속됨.
# 6. 예시
위에서 설명한 '생성자 호출 규칙', '생성자 상속' 개념을 적용한 예시를 살펴보면 개념을 쉽게 이해할 수 있을 거예요.
# 참고
이번 글은 여기서 마무리.
'Swift' 카테고리의 다른 글
KeyValuePairs (0) | 2024.01.28 |
---|---|
Memory Safety (1) | 2024.01.25 |
구조체(value type) 생성자에 대해 알아보자 (0) | 2024.01.17 |
private(set) (0) | 2024.01.17 |
@available (0) | 2023.12.05 |