class, structure, or enumeration의 instance를 준비하는 과정이다.
// syntax
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
// Customizing Initialization
// 파라미터에 따라 init()을 커스텀할 수 있다.
struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
struct Fahrenheit {
var temperature = 32.0 // default property values
}
옵셔널 프로퍼티는 인스턴스 생성 시, nil로 자동 초기화된다. (초기값이나 init()을 따로 구현해주지 않아도 된다)
class, sturct는 모든 프로퍼티를 직접 초기화해주면 자동으로 인수가 없는 init()을 갖는다.
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
print(item.name, item.quantity, item.purchased) // nil, 1, false
구조체 타입은 init()를 구현해주지 않아도 멤버 프로퍼티에 대한 init()을 자동으로 갖는다.
// default value가 없는 멤버를 포함한 자동 init 생성
struct Size {
var width: Double, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0) // 모든 멤버 초기화
let twoByZero = Size(width: 2.0) // 초기화되지 않은 멤버 초기화
// let height = Size(height: 2.0) // error: 초기화되지 않은 멤버 'width' missing
이니셜라이저의 파라미터 값이 잘못됐을 경우, 초기화에 실패할 수 있다. 이에 대응하기 위해 실패 시 nil을 반환하는 init?을 사용할 수 있다.
// 예제 코드
struct EmojiArt: Codable {
var url: URL
var emojis = [EmojiInfo]()
struct EmojiInfo: Codable {
let x: Int
let y: Int
let text: String
let size: Int
}
init?(json: Data) {
if let newValue = try? JSONDecoder().decode(EmojiArt.self, from: json) {
self = newValue
} else {
return nil
}
}
}
이니셜라이저가 또 다른 이니셜라이저를 호출할 수 있다. 이러한 과정을 Initializer Delegation라고 한다.
value type에서는 간단하게 self.init으로 delegation 할 수 있다.
Initializers can call other initializers to perform part of an instance’s initialization. This process, known as initializer delegation, avoids duplicating code across multiple initializers.
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)
self.init(origin: Point(x: originX, y: originY), size: size) // initializer delegation
}
}
Superclass로부터 상속받은 property를 포함한 class의 모든 stored property는 초기화되어야 한다. Superclass의 이니셜라이저를 자동으로 상속받는 경우가 아니라면 super.init()으로 superclass를 초기화 해주어야 한다.
All of a class’s stored properties—including any properties the class inherits from its superclass—must be assigned an initial value during initialization.
Rule 1
If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.
서브클래스가 이니셜라이저를 정의하지 않으면 수퍼클래스의 이니셜라이저를 자동 상속한다.
Rule 2
If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.
These rules apply even if your subclass adds further convenience initializers.
서브클래스가 수퍼클래스의 지정 이니셜라이저를 모두 구현한다면, 수퍼클래스의 convenience initializer를 자동 상속받는다. 이 규칙은 서브클래스가 convenience init을 추가하더라도 마찬가지로 적용된다.
// rule 1 example
class Vehicle {
var numberOfWheels: Int
init(numberOfWheels: Int) {
self.numberOfWheels = numberOfWheels
}
}
class Bicycle: Vehicle {
var withBasket: Bool = true // no error
}
var bicycle = Bicycle(numberOfWheels: 4) // 상속받은 이니셜라이저
class Hoverboard: Vehicle {
var color: String
init(color: String) { // define init
self.color = color
super.init(numberOfWheels: 2) // 반드시 self init 이후에 작성해야 한다.
}
}
NOTE
A subclass can implement a superclass designated initializer as a subclass convenience initializer as part of satisfying rule 2.
Superclass의 프로퍼티가 기본값으로 초기화되어 있는 경우는 subclass 초기화 시 암시적 super.init()이 일어난다.
class Vehicle {
var numberOfWheels = 0 // default init() 생성됨
}
// subclass 초기화 시 superclass의 init 암시적으로 일어난다.
class Hoverboard: Vehicle {
var color: String
init(color: String) {
self.color = color
// super.init() implicitly called here
}
}
init의 override도 가능하다. override 키워드를 init 앞에 붙여서 재정의할 수 있다.
class Vehicle {
var numberOfWheels = 0 // init() 자동 생성
}
class Bicycle: Vehicle {
override init() {
super.init() // superclass의 property value를 변경할 땐 super.init()
numberOfWheels = 2
}
}
convenience init으로 이니셜라이저를 위임할 수 있다.
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
// convenience init도 override 가능하다.
class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}
required init 을 사용하면 subclass 의 init 작성 시에도 required 키워드를 붙여야 하며,
subclass의 init이 호출될 때, superclass의 init도 자동 호출된다.
class SomeClass {
required init() {
// initializer implementation goes here
}
}
class SomeSubclass: SomeClass {
required init() {
// subclass implementation of the required initializer goes here
}
}
class만 구현할 수 있으며 인스턴스가 메모리에서 해제될 때 호출된다. deinit은 직접 호출할 수 없고 ARC 순서에 따라 자동 호출된다.
기본적으로는 deinit을 직접 구현할 필요가 없지만, class의 인스턴스가 해제될 때 추가로 하고 싶은 작업이 있을 경우 활용할 수 있다.
Superclass의 deinit은 subclass의 deinit이 호출되면 자동으로 호출된다.
https://docs.swift.org/swift-book/LanguageGuide/Initialization.html
https://www.edwith.org/boostcamp_ios/lecture/11310/
https://medium.com/@sonihemant111/required-initializers-in-swift
https://hcn1519.github.io/articles/2019-02/swift-init-basic