티스토리 뷰
실패 가능한 초기자 (Failable Initializers)
초기화 과정중 실패할 가능성이 있는 초기자를 init? 키워드를 사용해 표시할 수 있습니다.
NOTE
실패가능 초기자는 반환값으로 옵셔널 값을 생성합니다.
초기화에 실패하는 부분에서 return nil 을 작성해 초기화가 실패했다는걸 나타내줍니다.
비록 초기화가 실패했을때 return nil 을 써주지만, 성공했을 경우엔 return 키워드를 사용하지 않습니다.
아래는 실패 가능 초기자 Int(exactly:)를 사용한 예제 입니다.
알고가자
Int(exactly:) - 소수점 값이 0이면 정수만 추출하고, 소수점 값이 있으면 nil을 출력합니다.
ex)Int(exactly: 2.5) -> nil Int(exactly: 2.0) -> 2
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"
다음은 초기자에 입력값이 없으면 초기화 실패가 발생하도록 구현한 예제입니다.
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
// 초기값이 있을때
let someCreature = Animal(species: "Giraffe")
if let giraffe = someCreature {
print("An animal was initialized with a species of \(giraffe.species)")
}
// Print: An animal was initialized with a species of Giraffe
// 초기값이 없을때
let noCreature = Animal(species: "")
if noCreature == nil {
print("The no creature could not be initialized")
}
// Print: The no creature could not be initialized
열거형에 사용하는 실패 가능한 초기자
(Failable Initializers for Enumerations)
열거형에서도 실패 가능한 초기자를 사용할 수 있습니다.
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
}
}
}
위 예제에서 초기화시 지정된 특정 온도 표시단위 ("K", "C", "F")가 아닌 경우 초기화 결과로 nil을 반환 합니다.
아래를 확인해보시죠.
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, 초기화 성공.")
}
// Print: This is a defined temperature unit, 초기화 성공.
// TemperatureUnit symbol에는 "X" 가 없으니 nil을 반환 합니다.
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, 초기화 실패.")
}
// Print: This is not a defined temperature unit, 초기화 실패.
열거형에서 Raw 값을 사용하는 실패 가능한 초기자
(Failable Initializers for Enumerations with Raw Values)
enum이 가지고 있는 기능중 rawValue를 초기자 인자로 넣어 초기화에 사용할 수 있습니다.
아래와 같이 기능은 같지만 더 간결하게 사용할 수 있습니다. (switch문 필요x)
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, 초기화 성공.")
}
// Prints "This is a defined temperature unit, 초기화 성공."
let unknownUnit = TemperatureUnit(rawValue: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, 초기화 실패.")
}
// Prints "This is not a defined temperature unit, 초기화 실패."
초기자 실패의 생성 (Propagation of Initialization Failure)
실패 가능 초기자에서 실패가 발생하면 즉시 관련된 초기자가 중단 됩니다.
아래 예제를 보겠습니다.
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)
}
}
// 초기화 성공 (name 값도 있고, quantity도 1 보다 큼으로)
if let twoSocks = CartItem(name: "sock", quantity: 2) {
print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
}
// Print: Item: sock, quantity: 2
// 초기화 실패 (name 값은 있지만, quantity는 1보다 작으므로)
if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
} else {
print("Unable to initialize zero shirts")
}
// Print: Unable to initialize zero shirts
// 초기화 실패 (quantity는 1보다 크지만, name 값이 없으므로)
if let noNameProduct = CartItem(name: "", quantity: 5) {
print("Item: \(noNameProduct.name), quantity: \(noNameProduct.quantity)")
} else {
print("Unable to initialize zero shirts")
}
// Print: Unable to initialize zero shirts
실패 가능한 초기자의 오버라이딩 (Overriding a Failable Initializer)
아래처럼 초기자를 오버라이딩 할 수 있습니다.
상위클래스 하위클래스
실패가능한 초기자 ---> 실패불가능한 초기자
변환
※ 반대로는 불가능 합니다.
아래 Document 클래스는 초기화 할때, name값으로 특정 String을 지정하거나, nil을 지정할 수 있습니다.
name 값이 비어있는 (empty)의 경우엔 초기화 실패를 나타내는 nil을 반환합니다.
class Document {
var name: String?
init() {}
// 실패가능 초기자
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
Document의 하위클래스인 AutomaticallyNamedDocument 클래스에서는 기본 초기자와, 지정초기자를 override 해서 실패가능 초기자를 실패불가능 초기자로 만들었습니다.
초기값이 없는 경우엔 name에 기본값으로 Unnamed를 넣도록 했습니다.
class AutomaticallyNamedDocument: Document {
override init() {
super.init()
self.name = "Unnamed"
}
override init(name: String) {
super.init()
if name.isEmpty {
self.name = "Unnamed"
} else {
self.name = name
}
}
}
Document의 또다른 하위클래스 UnNamedDocument에서는 기본 초기자를 override 하여 초기자를 구현했고,
강제 언래핑 (! 느낌표)을 사용하여 해당 값이 옵셔널 값을 갖지 않도록 하였습니다.
super.init(name: "Unnamed")!
class UnnamedDocument: Document {
override init() {
super.init(name: "Unnamed")!
}
}
실패 가능한 init! 초기자 (The init! Failable Initializer)
실패 가능한 초기자 init? 을 init!로 오버라이딩 하거나, 위임하여 사용할 수 있습니다.
필수 초기자 (Required Initializers)
모든 하위클래스에서 반드시 구현해야 하는 초기자에는 required 키워드를 붙여줍니다.
class SomeClass {
required init() {
// initializer implementation goes here
}
}
필수초기자를 상속받은 하위클래스에서도 반드시 required 키워드를 붙여 다른 하위클래스에게도
이 초기자는 필수 초기자라는 것을 알려줘야 합니다.
class SomeSubclass: SomeClass {
required init() {
// subclass implementation of the required initializer goes here
}
}
NOTE
필수 초기자 표시를 해도 꼭 구현할 필요는 없습니다.
클로저나 함수를 이용해 기본 프로퍼티 값을 설정하기
(Setting a Default Property Value with a Closure or Function)
기본값 설정이 다소 복잡한 계산을 필요로 한다면, 클로저나 함수를 이용해 값을 초기화 하는데 이용할 수 있습니다.
기본값 지정을 위해 클로저를 사용하는 형태의 코드는 아래와 같습니다.
import UIKit
class SomeClass {
let testLabel: UILabel = {
let label = UILabel()
label.text = "여러 기본 값을 한꺼번에 할당할 수 있습니다."
label.textColor = .brown
label.font = .systemFont(ofSize: 30)
return label
}() // <- 초기화
}
testLabel은 클로저가 실행된 후 반환 타입이 UILabel인 label을 기본 값으로 갖게 됩니다.
초기화 내용이 정말 많습니다..
기껏해야 Model 만들때 init() 해봤던게 전부였는데,
추후에 작업해보면서 오늘 배운것들도 사용해봐야겠습니다.
'iOS' 카테고리의 다른 글
[iOS] TableView (programmatically) (3) | 2022.05.04 |
---|---|
[iOS] TextField 앞 뒤로 공백 있을시 제거 - trimmingCharacters(in:) (0) | 2022.05.01 |
[iOS] Operation (OperationQueue, BlockOperation, AsyncOperation) (0) | 2022.04.27 |
[iOS] label 속성 (0) | 2022.04.24 |
[Swift] 초기화 (Initialization) 2/3 - convenience, designated initializer (0) | 2022.04.22 |
- Total
- Today
- Yesterday
- removeLast()
- Swift final
- 2023년 회고
- Swift 알고리즘
- Swift init
- Swift ModernRIBs
- swift 고차함수
- Swift 내림차순
- Swift joined()
- Swift joined
- Swift Error Handling
- Swift Leetcode
- swift reduce
- Combine: Asynchronous Programming with Swift
- iOS error
- Class
- Swift 프로퍼티
- swift programmers
- Swift RIBs
- 원티드 프리온보딩
- RIBs tutorial
- RTCCameraVideoCapturer
- ios
- swift property
- CS 네트워크
- Swift 프로그래머스
- Swift
- Swift inout
- swift protocol
- swift (programmers)
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |