Initialization : class / struct / enum 의 instance 를 사용하기 위해 준비하는 과정. 각 stored property 의 초깃값을 설정하고, instance 가 사용되기 전에 필요한 다른 작업들을 수행함.
Initializer 를 통해 초기화 process 구현. Obj-C initializer 와는 다르게 Swift initializer 는 값을 리턴하지 않음. Initializer 의 주 목적은 처음 사용되기 전까지 instance 가 정확하게 초기화됐는지를 보장하는 것.
Class, struct 는 instance 가 생성될 때 모든 stored property 에 적절한 초깃값을 제공해야 함. Stored property 는 정의되지 않은 상태로 남겨지면 안됨.
Store property 의 초깃값은 initializer 를 통해서나 property 정의의 일부로 기본값을 할당할 수 있음
Stored property 에 기본값을 할당하거나 initializer 를 통해 값을 설정할 때 property 의 값은 바로 설정되고 property observer 가 호출되지 않음.
Initializer 를 사용해서 새로운 instance 생성. init 키워드를 붙임
init() {
// perform some initialization here
}
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".
Property 의 정의 부분에 default property value 를 제공할 수 있음.
만약 property 가 항상 같은 초깃값을 가진다면 initializer 내부에서 값을 설정하기 보단 default 값을 제공하는 것이 좋음. 결과는 항상 같지만, default value 는 initializer 보다 정의에 더 가까움.
Default value 는 코드를 더 간결하게 해주고 default value 로 property 의 type 을 추론할 수 있게 해줌. 또한 default initializer 나 initializer 상속에서도 이점을 제공함 (나중에 나옴)
struct Fahrenheit {
var temperature = 32.0 // default value 제공
}
Input parameter, optional property type, initialization 과정에서 constant property 할당 등을 통해 초기화 process 를 customizing 할 수 있음
Initialization parameter 를 initializer 의 정의 부분에 추가해서 초기화 과정을 custom 할 수 있음.
struct Celsius {
var temperatureInCelsius: Double
// custom initializer 1
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
// custom initializer 2
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
// boilingPointOfWater.temperatureInCelsius is 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius is 0.0
Function, method parameter 와 마찬가지로 initialization parameters 는 argument label (initializer 를 호출할 때 사용되는 label) 과 parameter name (initializer body 에서 사용되는 이름) 을 모두 가질 수 있음
Initializer 는 특별히 이름이 없기 때문에 parameter 는 어떤 initializer 가 호출되즌지 구분하기 위해 중요한 역할을 함. 따라서 Swift 는 만약 argument label 를 임의로 지정하지 않으면 자동으로 모든 parameter 에 argument label 을 제공함
struct Color {
let red, green, blue: Double
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
init(white: Double) {
red = white
green = white
blue = white
}
}
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
let halfGray = Color(white: 0.5)
// initializer 의 argument label 없이 호출할 수 없음
let veryGreen = Color(0.0, 1.0, 0.0)
// ❗️ this reports a compile-time error - argument labels are required
만약 initializer paramter 에 argument label 를 원하지 않는 경우 underscore _ 을 써서 명시적인 argument label 을 사용하지 않도록 할 수 있음
struct Celsius {
var temperatureInCelsius: Double
init(_ celsius: Double) {
temperatureInCelsius = celsius
}
}
let bodyTemperature = Celsius(37.0)
// bodyTemperature.temperatureInCelsius is 37.0
Custom type 이 stored property 를 갖고 있고 ‘no value’ 상태인 게 논리적으로 허용되는 상태라면 (초기화 도중에 값이 설정될 수 없는 상태라면) optional type 으로 property 정의.
Optional type property 는 자동으로 nil 로 초기화 되며, property 가 초기화 도중 ‘no value’ 를 가질 수 있는 것을 드러냄.
class SurveyQuestion {
var text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
}
}
let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")
cheeseQuestion.ask()
// Prints "Do you like cheese?"
cheeseQuestion.response = "Yes, I do like cheese."
초기화 도중에 constant property 에 값을 할당할 수 있음. 한 번 값이 할당되면 추후 수정할 수 없음.
Class instance 의 경우 constant property 는 정의된 class 의 초기화 과정에서만 수정될 수 있고, subclass 에서 수정할 수 없음
class SurveyQuestion {
let text: String
var response: String?
init(text: String) {
// constant property initializer 내부에서 수정
self.text = text
}
func ask() {
print(text)
}
}
let beetsQuestion = SurveyQuestion(text: "How about beets?")
beetsQuestion.ask()
// Prints "How about beets?"
beetsQuestion.response = "I also like beets. (But not with cheese.)"
Swift 는 initializer 를 하나도 제공하지 않는 struct / class 가 모든 property 에 기본값이 부여되어 있다면 default initializer 를 제공함. Default initializer 는 모든 property 를 default 값에 할당해서 새로운 instance 를 생성하는 initializer.
// 모든 property 가 default value 가 있고, superclass 가 없는 base class 이기 때문에 해당 class 는 자동으로 default initializer 구현을 가짐.
class ShoppingListItem {
var name: String? // 기본값이 nil
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
Struct 는 custom initializer 를 아무것도 정의하지 않은 경우 자동으로 memberwise initializer 를 가짐. Struct 는 stored property 가 default value 를 가지지 않는 경우에도 memberwise initialzier 를 가짐.
// 자동으로 init(width:height:) memberwise initializer 를 가짐
struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
Memberwise initializer 를 호출할 경우 default value 가 있는 property 의 경우 initializer 를 호출할 때 제외할 수 있음.
// omit width
let zeroByTwo = Size(height: 2.0)
print(zeroByTwo.width, zeroByTwo.height)
// Prints "0.0 2.0".
// omit all
let zeroByZero = Size()
print(zeroByZero.width, zeroByZero.height)
// Prints "0.0 0.0".
Initializer delegation : Initializer 는 내부에 다른 initializer 를 호출하는 것. 이를 통해 여러 initializer 에 걸쳐 나타나는 반복되는 코드를 줄일 수 있음
Initializer delegation 이 동작하는 규칙 등은 value type, class type 에서 다름.
self.init 을 사용해서 같은 타입 내 다른 initializer 호출Value type 에 custom initializer 를 정의하면 더 이상 default initializer/memberwise initializer 에 접근할 수 없음. 이런 제약은 자동으로 생성된 initializer를 잘못 사용하는 것을 방지함
struct Rect {
var origin = Point()
var size = Size()
init() {}
init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
// init(origin:size:) initializer call/delegate
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
만약 custom value 타입이 default initializer 와 memberwise initializer를 가지면서 직접 정의한 custom initializer 를 가지게 하고 싶으면 custom initializer 를 extension 에 정의하면 됨
struct Person {
var name: String = "YJ"
}
extension Person {
init(someCustomName: String) {
name = "[\(someCustomName)]"
}
}
// 모두 가능
let person1 = Person()
let person2 = Person(name: "Person2")
let person3 = Person(someCustomName: "Person3")
모든 class 의 stored property (superclass 에서 상속받은 property 포함) 은 초기화 도중에 초깃값이 할당되어야 함
Swift 는 모든 stored property 가 초깃값을 갖는 것을 보장하기 위해 두 종류의 initializer 를 제공함.
Class 는 보통 적은 수의 Designated initializer 를 가지고, 일반적으로 하나만 가짐. Designated initializer 는 초기화가 진행되는 ‘깔때기’ point 를 제공하며 초기화가 superclass chain 을 타도록 하는 point 를 제공함
모든 class 는 최소 하나의 designated initializer 를 가짐. 어떤 경우에 이는 superclass 에서 하나 이상의 designated initializer 를 상속해서 조건이 충족됨
필요하지 않다면 class 에서 convenience initializer 를 제공하지 않아도 됨. 흔한 초기화 패턴을 위한 shortcut 이 시간을 절약하거나 의도에 맞는 초기화가 필요한 경우 convenience initializer 생성
// Designated initialzier
init(<#parameters#>) {
<#statements#>
}
// convenience initializer
convenience init(<#parameters#>) {
<#statements#>
}
Swift 는 initializer 간의 3가지 delegation call 규칙을 따름.


Swift 에서 class 초기화는 두 단계 process 를 거침.
두 단계 초기화를 통해
Swift compiler 는 4개의 safety-check 을 통해 두 단계 초기화가 에러 없이 완료될 수 있게 함
self 를 값으로 참조할 수 없음Class 의 instance 는 첫 번째 단계가 끝날 때까지 유효하지 않음. (첫 번째 단계가 끝나야 property 접근 / method 호출을 할 수 있음)