실패할 수 있는 초기화 프로그램
init키워드 뒤에 ? 를 붙여서 실패가능한 이니셜라이저를 정의할 수 있다. 마치 옵셔널처럼!
init?() {
}
init?(name: String) {
guard name.count > 2 else {return nil}
}
초기화 프로그램은 값을 반환하지 않지만 실패가능한 이니셜라이저에서 초기화 실패가 트리거 될 수 있도록 nil을 반환 하도록 할 수 있다.
성공한다면 반환값을 두지 않도록 한다.
당연히 실패가능한 이니셜라이저와 지정이니셜라이저의 파라메터명이 똑같을수 없다.
init(name: String) {}
init?(name: String) {} //같은 형식으는 안돼
아래 사용예시를 보면 이해가 쉽다.
let wholeNumber: Double = 12345.0 //가능
let pi = 3.14159 //불가
if let valueMaintained = Int(exactly: wholeNumber) {
print("\(wholeNumber) conversion to Int maintains value of \(valueMaintained)")
}
// Prints "12345.0 conversion to Int maintains value of 12345"
let valueChanged = Int(exactly: pi)
// valueChanged is of type Int?, not Int
if valueChanged == nil {
print("\(pi) conversion to Int doesn't maintain value")
}
// Prints "3.14159 conversion to Int doesn't maintain value"
열거형도 실패가능한 이니셜라이저를 사용할 수 있다.
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 fahrenheit = TemperatureUnit(symbol: "F") //fahrenheit == .fahrenheit
let unknownUnit = TemperatureUnit(symbol: "X") //nil 초기화 실패
따로 우리가 정의하지 않아도 아래와 같은 상황을 자주 봤을 것이다.
열거형의 초기화는 a,b,c에 해당하는 정수값이 들어오지 못한다면 실패가능한 이니셜라이저를 통해 nil이 반환되도록 이미 만들어져 있다.
실패가능한 초기화의 전파
실패할 수 있는 이니셜라이저는 동일한 유형으로 실패 할 수 있는 이니셜라이저에 위임이 가능하다.
또 하위 클래스 실패 가능 이니셜라이저는 상위 클래스의 실패가능한 이니셜라이저까지 위임할 수 있다.
아래 코드를 보자.
class Product {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
class CartItem: Product {
let quantity: Int
init?(name: String, quantity: Int) {
if quantity < 1 { return nil }
self.quantity = quantity
super.init(name: name)
}
}
CartIem은 Product를 상속받은 상태이며 실패가능한 이니셜라이저를 구현했다.
특이한건 상위 클래스에도 init?이 있고 하위클래스에서 이를 호출하고 있다는건데 딱 봐도 감이 온다.
정상적인 CartItem 인스턴스를 만들기 위해선 CardItem의 이니셜라이저의 quantity의 값이 1과 같거나 커야 한다는 조건을 만족시키고
상위 클래스인 Product의 이니셜라이저에선 name.isEmpty 가 아니어야 한다.
두 조건을 차례로 만족시켜야 정상적인 CartItem의 인스턴스를 생성할 수 있다.
만약 아래와 같이 시도했다면?
let item = CartItem(name: "", quantity: 2)
반환값은 nil이 될 것이다. CartItem의 quantity 조건은 만족했지만 상위 클래스의 조건은 만족하지 못했기 때문이다.
조건을 모두 만족하기 위해선 아래처럼 하면 된다.
let item = CartItem(name: "Teemo", quantity: 2)
실패할수 있는 초기화 프로그램 재정의
실패 가능한 이니셜라이저를 하위 클래스에서 실패하지 않는 이니셜라이저로 재정의 할 수 있다.
반대로 실패하지 않는 이니셜라이저를 실패할 수 있는 이니셜라이저로 재정의 할 수는 없다.
class Document {
var name: String?
// this initializer creates a document with a nil name value
init() {}
// this initializer creates a document with a nonempty name value
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
class AutomaticallyNamedDocument: Document {
override init() {
super.init()
self.name = "[Untitled]"
}
override init(name: String) {
super.init()
if name.isEmpty {
self.name = "[Untitled]"
} else {
self.name = name
}
}
}
AutomaticallyNamedDocument의 지정이니셜라이저 init(name:)은 Document의 실패가능한 이니셜라이저를 nonFailable이니셜라이저로 재정의했다.
이때 하위클래스의 init(name: String)에서 상위클래스의 실패가능한 이니셜라이저 super.init(name:)를 호출 시도하면 컴파일러는 에러를 발생시킨다.
재정의 시 실패가능한 이니셜라이저로 만든게 아니므로 해당 이니셜라이저는 반드시 초기화에 성공해야만 한다. 만약 상위클래스의 실패가능한 이니셜라이저를 호출하게 된다면 재정의된 이니셜라이저에 실패가능성이 내재되는것이기 때문
강제언래핑으로 super.init(name: name)을 nil이 아님을 보장하거나, init? 으로 변경하여 실패가능한 이니셜라이저가 되도록 해야만 한다.
강제언래핑을 통해 초기화 성공을 보장하게되면?
하위클래스의 이니셜랑이저에서 강제 언래핑을 통해 실패할수 있는 상위클래스의 이니셜라이저를 호출할 수 있다. 다만 초기화 성공을 보장해야하는 만큼! 상위클래스의 실패할 수 있는 이니셜라이저에서 초기화실패가 발생한다면 런타임 에러가 발생한다.
class untitledDocument: Document {
override init() {
super.init(name: "[Untitled]")!
}
}
위 코드에서 상위클래스의 이니셜라이저로 전달하는 문자열을 빈 문자열로 바꿔본다면 런타임 에러가 발생한다.
필수 이니셜라이저
클래스의 모든 하위 클래스에서 꼭 구현해야할 이니셜라이저가 있을 경우 required키워드를 사용한다.
필수 이니셜라이저는 선택요소인 재정의가 아니므로 하위 클래스에서 모두 구현해야한다.
class SomeClass {
required init(name: String) {
}
}
class SomeSubClass: SomeClass {
required init(name: String) {
}
//이니셜라이저 상속 규칙에 따른다.
//예) SomeSubClass는 required init을 구현하지 않아도 이니셜라이저가 자동상속 되므로 사용할 수 있다.
}
'난 iOS개발자 > iOS' 카테고리의 다른 글
String을 Subscript로 접근할 수 없는 이유 (0) | 2022.05.04 |
---|---|
Class의 성능을 향상 시키는 방법 (0) | 2022.05.03 |
initialization-2 (0) | 2022.04.27 |
initialization-1 (0) | 2022.04.25 |
Method( Instance Method, Type Method) (0) | 2022.03.16 |