스터디를 진행하며 처음부터 다시 Swift를 공부하고 있습니다.
오늘 작성할 파트는 초기화 입니다.
초기화는 내용도 많고 난이도도 높은 것 같습니다..
일단 기초적인 내용은 다 추가하였습니다.
심화적인 부분? org 기준 후반부분은 추가가 필요합니다.
초기화 (Initialization)는 인스턴스의 클래스, 구조체, 또는 열거형을 사용하기 위해 준비하는 단계이다.
이 단계에는 인스턴스에 각 저장된 프로퍼티에 초기값을 설정하고, 새로운 인스턴스가 사용할 준비가 되기 전에 다른 설정이나 초기화를 수행하는 것을 포함한다.
init() {
// perform some initialization here
}
구조체 & 클래스의 경우
해당 예시에서는 3번 방법을 사용하였다. ( 구조체 )
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature)° Fahrenheit")
// Prints "The default temperature is 32.0° Fahrenheit"
프로퍼티가 항상 같은 초기값을 갖는다면 초기화 구문 내에서 값을 설정하기보다 기본값을 제공하라
이것은 초기화 구문을 더 짧고, 명확하게 하고 기본값으로 부터 프로퍼티의 타입을 유추할 수 있다.
초기화는 모든 프로퍼티가 값을 가져야만 초기화에 성공해서 인스턴스를 생성한다.
그런데 구조체는 프로퍼티의 초기화를 따로 지정하지 않을 경우, 초기화되지 않은 프로퍼티를 초기화할 수 있는 init 함수를 자동으로 제공하기 때문에 이렇게 할 수 있다. ( memberwise initializers )
struct Fahrenheit {
var temperature: Double
}
var f = Fahrenheit(temperature: 32.0)
print("The default temperature is \(f.temperature)° Fahrenheit")
// Prints "The default temperature is 32.0° Fahrenheit"
하지만 클래스의 경우 memberwise initializers를 제공하지 않아서.. 초기값 지정을 구조체와는 좀 다르게 해줘야 한다.
크게 두가지로 나뉘는데,
Designated Initializers
(지정생성자) : 클래스의 모든 프로퍼티를 초기화 하는 생성자
지정 생성자의 경우 옵셔널 변수로 선언 되지 않았거나, 기본 값을 갖지 않는 프로퍼티가 있을 경우엔 생성자를 추가하여 초기값을 지정한다. 또한 상속받은 서브 클래스에서 지정 생성자를 작성할 경우, 반드시 슈퍼 클래스의 Initializers를 호출해주어야 한다.
class Fahrenheit {
let temperature : String
init(temperature : String) {
self.temperature = temperature
}
}
class Temp : Fahrenheit {
var feel : String
init(feel : String) {
self.feel = feel
super.init(temperature : feel)
}
}
Convenience Initializers
(편의 생성자) : Designated Initializers를 도와주는 역할
Convenience Initializers는 보조 생성자라, 굳이 모든 프로퍼티를 초기화 할 필요가 없다.
허나, 만약 초기화가 안 된 프로퍼티가 있는 경우 지정 생성자가 모든 프로퍼티를 초기화 시켜서
최종적으로 같은 클래스 내에 있는 지정 생성자를 호출 시키기 때문에,
아무리 보조 생정자를 호출해도 어짜피 마지막에 지정 생성자를 호출하기에 신경쓰지 않아도된다.
class Fahrenheit {
let temperature : String
let feels : String
init(temperature : String, feels : String) {
self.temperature = temperature
self.feels = feels
}
convenience init(temperature : String) {
self.init(temperature: temperature, feels: "cold")
}
}
예시로 설명하자면, Convenience 생성자가 먼저 실행되고 그 다음 최종적으로 Designated 생성자가 실행되는 순서인 것이다.
그렇기 때문에 꼭 Designated 생성자가 생성되어 있어야 에러가 나지 않으니 주의!!
( 내용이 좀 많이 어렵다.. Convenience랑 Designated 이해하는데 시간 오래걸림.. )
2단계 초기화는 프로퍼티 값이 초기화 되기전에 접근하는 것을 막고 다른 초기화 구문이 예기치 않게 다른 값을 설정하는 것을 막는다. 때문에 초기화를 보다 안전하게 진행하기 위해 2단계 초기화를 사용하는 것이다.
예시로 1단계와 2단계를 해설해보겠다.
class Human {
var name: String
init(name: String) {
self.name = name
print(name)
}
}
class Developer: Human {
var language: String
init(language: String) {
self.language = language
super.init(name: "nothing")
print(language)
}
}
class Dtzero : Developer {
var nickName: String
init(nickName: String) {
self.nickName = nickName
super.init(language: "swift")
print(nickName)
}
}
1단계 초기화
위의 예시에서 Dtzero란 클래스의 인스턴스를 생성하기 위해 생성자를 부르게 되면
Dtzero(nickName: "well")
Dtzero → Developer → Human 순서로 자신의 프로퍼티를 초기화하고
super.init
을 통해 부모 클래스까지 초기화 시키면 1단계 초기화는 끝이난다.
2단계 초기화
모든 프로퍼티가 기본 값을 가지게 되어 초기화가 완료 되었다면,
가장 마지막으로 호출됐던 제일 상위 클래스의 남은 작업부터 차례로 아래로 실행된다. ( 스택 )
해서 위의 Dtzero란 클래스의 인스턴스를 생성하기 위해 생성자를 부르면 이렇게 출력 된다.
/*
nothing
swift
well
*/
nothing
이 Human 클래스의 name이고, swift
가 Developer 클래스의 language, well
이 Dtzero 클래스의 nickName이다. ( 2차 초기화 과정에서 값들이 덮어 씌워진 것 )
Convenience
는 상속은 되지만 재정의가 불가능
자식 클래스에서 재정의 초기화 할 때 반드시 super.init
필수!
부모 클래스의 모든 지정 초기화를 상속받거나,
부모 클래스의 모든 지정 초기화를 오버라이딩 해서 구현 했다면
이땐 부모의 모든 편의 초기화 메서드가 상속된다.
클래스 초기화 구문 앞에 required
를 작성하여 클래스의 모든 하위 클래스가 해당 초기화 구문을 구현해야 함을 나타낸다. 추가로 지정된 필수 초기화 구문을 재정의 할 때 override
를 작성하지 않는다.
class SomeClass {
required init() {
// initializer implementation goes here
}
}
class SomeSubclass: SomeClass {
required init() {
// subclass implementation of the required initializer goes here
}
}