Initialization
은 class, structure, enumeration의 인스턴스를 준비하는 프로세스이다.
이 인스턴스의 stored property에 대한 초기값을 설정하고, 새 인스턴스를 사용할 준비가 되기 전에 필요한 다른 설정 및 초기화를 수행한다.
Initializer
를 통해 새 인스턴스를 만들며 Objective-C
와 달리 Swift
의 initializer는 값을 반환하지 않는다. initializer의 역할은 새 인스턴스가 처음 사용되기 전에 올바르게 초기화되었는지 확인하는 것이다.
class type의 Instances는 deinitializer를 구현할 수 있다.
class와 structure는 인스턴스가 생성될 때 무조건 Stored Properties에 대한 Initial Value를 가지고 있어야한다.
Initial Value는 initializer내에서 설정할 수도 있고, default property value를 할당할 수도 있다.
이때 property observer를 호출하지 않고 direct하게 세팅된다.
struct Resolution {
var width = 0
var height = 0
init(width: Int) {
self.width = width
}
// init은 파라미터에 따라 오버로딩이 가능하다.
init(height: Int) {
self.height = height
}
init(width: Int, height: Int) {
self.width = width
self.height = height
}
}
상속받은 모든 부모의 init을 호출한다.
class Base {
var base: Double
init() {
self.base = 0.0
print("Base Init")
}
}
class ExBase: Base {
// 오버라이드 된 init 실행 후 부모의 init 실행
// 부모의 init을 먼저 실행하려면 super.init()을 명시적으로 호출해야함
override init() {
//super.init()
print("ExBase Init")
}
}
let ex = ExBase()
// ExBase Init
// Base Init
// super.init()이 있을 경우
// Base Init
// ExBase Init
struct Question {
var text: String
var respond: String?
init(text: String) {
self.text = text
}
init(text: String, respond: String) {
self.text = text
self.respond = respond
}
}
var question = Question(text: "Optional?")
question.respond = "No"
print(question.respond) // Optional(No)
class Shopping {
var name: String?
var quantity = 1
var purchased = false
}
// 모든 프로퍼티의 초기값이 설정되어있고, 초기자가 정의되지 않았을 경우 기본 initializer 제공
let shop = Shopping()
struct Size {
var width: Int
var height = 0
}
// Class 와 달리 Struct는 프로퍼티의 기본 값이 없어도 Memberwise Initializer 제공
var size = Size(width: 4, height: 6)
init(parameters) {
statements
}
convenience init(parameters) {
statements
}
더 복잡한 형태의 delegate Init
1단계 : 각 stored property는 초기값으로 초기화 된다.
모든 stored property의 상태가 결정되면 두번째 단계가 시작된다.
2단계 : 새로운 인스턴스의 사용이 준비되었다고 알려주기 전, stored property를 커스터마이징한다.
Swift의 컴파일러는 2단계 초기화가 에러없이 끝나는 것을 보장하기 위해 4단계 안전 확인(safety-check)을 한다.
안전 확인 1단계 지정 초기자는 클래스 안에서 초기화를 superclass의 초기자에게 위임하기 전에 모든 프로퍼티를 초기화 해야 합니다. 위에서 언급한 것 처럼 메모리에서 객체는 모든 저장된 프로퍼티가 초기 상태를 갖어야만 완전히 초기화 된것으로 간주되기 때문에 이 규칙을 만족시키기 위해 지정 초기자는 반드시 다른 초기자로 넘기기 전에 소유하고있는 모든 프로퍼티를 초기화 해야 합니다.
안전 확인 2단계 지정 초기자는 반드시 상속된 값을 할당하기 전에 superclass의 초기자로 위임을 넘겨야 합니다. 그렇지 않으면 상속된 값이 superclass이 초기자에 의해 덮어 쓰여지게 됩니다.
안전 확인 3단계 편리한 초기자는 반드시 어떤 프로퍼티를 할당하기 전에 다른 초기자로 위임을 넘겨야 합니다. 만약 그렇지 않으면 편리한 초기자에 의해 할당된 값을 다른 클래스의 지정 초기자에 의해 덮어 쓰여지게 됩니다.
안전 확인 4단계 이니셜라이저는 초기화의 1단계가 끝나기 전에는 self의 값을 참조하거나 어떤 인스턴스 프로퍼티, 메소드 등을 호출하거나 읽을 수 없습니다.
Swift에서는 기본적으로 subclass에서 superclass의 이니셜라이저를 상속하지 않습니다.
따라서 Automatic Initializer Inheritance에 의해 서브클래스가 지정 초기자를 정의하지 않으면 자동으로 슈퍼클래스의 모든 지정 초기자를 상속한다.
또한 서브클래스가 수퍼클래스의 지정초기자를 모두 구현한 경우 자동으로 슈퍼클래스의 convenience Initializer를 추가한다.
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
let namedMeat = Food(name: "Bacon")
// namedMeat's name is "Bacon"
let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]"
서브 클래싱의 예
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)
}
}
이렇게 선언 가능
let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
초기화 과정 중에 실패할 가능성이 있는 초기자를 init뒤에 물음표(?)를 사용해 실패가 가능한 초기자라고 표시할 수 있습니다.
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
let fail = Animal(species: "") // nil
enum TemperatureUnit {
case kelvin, celsius, fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .kelvin
case "C":
self = .celsius
case "F":
self = .fahrenheit
default:
return nil
}
}
}
let temp = TemperatureUnit(symbol: "C")! // prints celsius
let temp2 = TemperatureUnit(symbol: "A") // nil
enum TemperatureUnit: Character {
case kelvin = "K", celsius = "C", fahrenheit = "F"
}
let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// Prints "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(rawValue: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// Prints "This is not a defined temperature unit, so initialization failed."
모든 서브클래스에서 반드시 구현해야 하는 초기자에는 아래 예제와 같이 required 키워드를 붙여 줍니다.
필수초기자 표시를 해도 꼭 구현을 할 필요는 없습니다.
class SomeClass {
required init() {
// initializer implementation goes here
}
}
struct Chessboard {
let boardColors: [Bool] = {
var temporaryBoard = [Bool]()
var isBlack = false
for i in 1...8 {
for j in 1...8 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
return temporaryBoard
}()
func squareIsBlackAt(row: Int, column: Int) -> Bool {
return boardColors[(row * 8) + column]
}
}
let board = Chessboard()
print(board.squareIsBlackAt(row: 0, column: 1))
// Prints "true"
print(board.squareIsBlackAt(row: 7, column: 7))
// Prints "false"