지정 이니셜라이저
와 편의 이니셜라이저
로 역할을 구분한다.지정 이니셜라이저(Designated Initializer)
부모클래스의 이니셜라이저를 호출할 수
있으며, 이니셜라이저가 정의된 클래스의 모든 프로퍼티를 초기화
해야 하는 임무를 가지고 있다.클래스에 하나 이상 정의
한다.편의 이니셜라이저(Convenience Initializer)
초기화를 좀 더 손쉽게 도와주는 역할
을 한다.지정 이니셜라이저
를 자신 내부에서 호출
한다.항상 같은 값으로 초기화가 가능
하다.[지정 이니셜라이저]
init(매개변수들) {
초기화 구문
}
[편의 이니셜라이저]
convenience init(매개변수들) {
초기화 구문
}
부모클래스의 지정 이니셜라이저를 반드시 호출
해야 한다.자신을 정의한 클래스의 다른 이니셜라이저를 반드시 호출
해야 한다.지정 이니셜라이저를 반드시 호출
해야 한다. 스위프트의 클래스 초기화는 2단계(two-phase)를 거친다.
1단계
초깃값이 할당
된다.2단계
2단계 초기화는 프로퍼티를 초기화하기 전에 프로퍼티 값에 접근하는 것을 막아 초기화를 안전하게
할 수 있도록 해준다.
다른 이니셜라이저가 프로퍼티의 값을 실수로 변경하는 것을 방지
할 수도 있다.
스위프트 컴파일러는 2단계 초기화를 오류 없이 처리하기 위해 다음과 같은 네 가지 안전확인
(Safty-Checks)을 실행한다.
자식클래스
의 지정 이니셜라이저
가 부모클래스의 이니셜라이저를 호출하기 전에 자신의 프로퍼티를 모두 초기화했는지 확인
한다.자식클래스
의 지정 이니셜라이저
는 상속받은 프로퍼티에 값을 할당하기 전
에 반드시 부모클래스의 이니셜라이저를 호출
해야 한다.편의 이니셜라이저
는 자신의 클래스에 정의한 프로퍼티를 포함하여 그 어떤 프로퍼티라도 값을 할당하기 전에 다른 이니셜라이저를 호출
해야 한다.위의 네 가지 안전확인에 근거하여 어떻게 2단계 초기화가 이루어지는지
살펴보도록 한다.
1단계
클래스가 지정 또는 편의 이니셜라이저를 호출한다.
그 클래스의 새로운 인스턴스를 위한 메모리가 할당된다. 메모리는 아직 초기화되지 않은 상태이다.
지정 이니셜라이저는 클래스에 정의된 모든 저장 프로퍼티에 값이 있는지 확인한다. 현재 클래스 부분까지의 저장 프로퍼티를 위한 메모리는 이제 초기화되었다.
지정 이니셜라이저는 부모클래스의 이니셜라이저가 같은 동작을 행할 수 있도록 초기화를 양도한다.
부모클래스는 상속 체인을 따라 최상위 클래스에 도달할 때까지 이 작업을 반복한다.
2단계
1.최상위 클래스로부터 최하위 클래스까지 상속 체인을 따라 내려오면서 지정 이니셜라이저들이 인스턴스를 제 각각 사용자 정의하게 된다. 이 단계에서는 self를 통해 프로퍼티 값을 수정할 수 있고, 인스턴스 메서드를 호출하는 등의 작업을 진행할 수 있다.
2.마지막으로 각각의 편의 이니셜라이저를 통해 self를 통한 사용자 정의 작업을 진행할 수 있다.
class Person {
var name: String = ""
var age: Int = 0
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
class Student: Person {
var major: String = "F"
init(name: String, age: Int, major: String) {
self.major = major
super.init(name: name, age: age)
}
convenience init(name: String) {
self.init(name: name, age: 4, major: "")
}
func test() { print("test") }
}
super.init(name: name, age: age)
를 통해 부모클래스의 이니셜라이저를 호출했으며 그 외에 상속받은 프로퍼티가 없으므로 부모의 이니셜라이저 호출 이후에 값을 할당해줄 프로퍼티가 없다. → 안전확인 중 2번의 조건을 만족한다.super.init(name: name, age: age)
아래에 self.test()
메서드를 호출하면 호출 가능해진다.
→ 안전확인 중 4번의 조건을 만족한다.
재정의
하면 된다. 그러려면 override
수식어를 붙여야 한다. 자식클래스의 편의 이니셜라이저가 부모클래스의 지정 이니셜라이저를 재정의하는 경우에도 override
수식어를 붙여주면 된다.override
수식어를 붙이지 않는다. 자식클래스에서 부모클래스의 편의 이니셜라이저는 절대로 호출할 수 없기 때문
이다. 즉, 재정의할 필요가 없다.class Person {
var name: String = ""
var age: Int = 0
init(name: String, age: Int) {
self.name = name
self.age = age
}
convenience init(name: String) {
self.init(name: name, age: 0)
}
}
class Student: Person {
var major: String = "F"
override init(name: String, age: Int) {
self.major = "Swift"
super.init(name: name, age: age)
}
convenience init(name: String) {
self.init(name: name, age: 4)
}
}
특정 조건
에 부합한다면 부모클래스의 이니셜라이저가 자동으로 상속된다.자식클래스에서 프로퍼티 기본값을 모두 제공한다고 가정
할 때, 다음 두 가지 규칙에 따라 이니셜라이저가 자동으로 상속된다. **규칙1**
: 자식클래스에서 별도의 지정 이니셜라이저를 구현하지 않는다면, 부모클래스의 지정 이니셜라이저가 자동으로 상속된다. **규칙2**
: 만약 규칙1
에 따라 자식클래스에서 부모클래스의 지정 이니셜라이저를 자동으로 상속받은 경우 또는 부모클래스의 지정 이니셜라이저를 모두 재정의하여 부모클래스와 동일한 지정 이니셜라이저를 모두 사용 할 수 있는 상황이라면 부모클래스의 편의 이니셜라이저
가 모두 자동으로 상속된다.class Person {
var name: String = ""
var age: Int = 0
init(age: Int) {
self.age = age
}
init(name: String, age: Int) {
self.name = name
self.age = age
}
convenience init(name: String) {
self.init(name: name, age: 0)
}
}
class Student: Person {
var major: String = "F"
}
class Developer: Person {
var company: String
//부모클래스의 지정이니셜라이저를 자식클래스의 편의이니셜라이저로 재정의
override convenience init(age: Int) {
self.init(name: "developer", age: age)
self.company = "Unknown"
}
//부모클래스의 지정이니셜라이저 재정의
override init(name: String, age: Int) {
self.company = "Unknown"
super.init(name: name, age: age)
}
//자식클래스의 지정 이니셜라이저
init(name: String, age: Int, company: String) {
self.company = company
super.init(name: name, age: 0)
}
//자식클래스의 편의 이니셜라이저
convenience init(company: String) {
self.init(name: "Unknown", age: 0)
self.company = company
}
}
//부모클래스의 지정 이니셜라이저 자동 상속
let yagom: Person = Person(name: "야곰", age: 5)
let haha: Student = Student(name: "하하", age: 20)
print(yagom.name)
print(haha.name)
print()
//부모클래스의 편의 이니셜라이저 자동 상속
let wizplan: Person = Person(name: "wizplan")
let jinsung: Student = Student(name: "jinsung")
print(wizplan.name)
print(jinsung.name)
print()
//부모클래스의 편의 이니셜라이저 자동 상속
let developer: Developer = Developer(name: "개발자")
print(developer.name)
print(developer.company)
print(developer.age)
print()
//부모클래스의 지정 이니셜라이저를 자식클래스에서 편의 이니셜라이저로 재정의
let developer2: Developer = Developer(age: 55)
print(developer2.name)
print(developer2.company)
print(developer2.age)
print()
//자식클래스에서 편의 이니셜라이저를 정의해도 자동 상속에 문제가 없음.
let developer3: Developer = Developer(company: "Naver")
print(developer3.name)
print(developer3.company)
print(developer3.age)
--result--
야곰
하하
wizplan
jinsung
개발자
Unknown
0
developer
Unknown
55
Unknown
Naver
0
프로퍼티에 기본값이 있으며
, 따로 지정 이니셜라이저를 구현해주지 않았으므로 부모클래스인 Person클래스의 지정 이니셜라이저가 자동으로 상속됨.규칙1
에 부합한다.편의 이니셜라이저도 자동으로 상속
되었다.규칙1
에 부합한다.규칙2
를 충족한다.required
수식어를 클래스의 이니셜라이저 앞에 명시해주면 이 클래스를 상속받은 자식클래스에서 반드시 해당 이니셜라이저를 구현해주어야 한다.override
수식어 대신에 required
수식어를 사용한다.class Person {
var name: String
required init() {
self.name = "Unknown"
}
}
class Student: Person {
var major: String = "Unknown"
}
let miJeong: Student = Student()
class Person {
var name: String
required init() {
self.name = "Unknown"
}
}
class Student: Person {
var major: String = "Unknown"
init(major: String) {
self.major = major
super.init()
}
required init() {
self.major = "Unknown"
super.init()
}
}
let miJeong: Student = Student()
required override
를 명시해주어 재정의됨과 동시에 요구 이니셜라이저가 될 것임을 명시해준다.required convenience
를 명시해주어 재정의됨과 동시에 요구 이니셜라이저가 될 것임을 명시해준다.