C나 Objective C에는 없는 개념인 Optional에 대해 알아보도록 하겠습니다. Optional은 Generic Enumeration으로 선언되어 있으며, wrapped value나 nil을 나타내는 타입입니다. Optional은 해당 변수에 값이 없을 것(nil) 같을 때 사용할 수 있습니다.
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber는 Int?나 Optional<Int>로 형식이 추론됩니다.
String인 "123"을 Int로 캐스팅하면 당연히 123이 나오겠다고 생각하지만 캐스팅은 실패할 수도 있습니다. 그렇기 때문에 캐스팅 결과는 Optional 타입으로 반환되게 됩니다. Optional 타입은 간단하게 타입뒤에 물음표를 붙여 나타낼 수 있습니다.
let shortForm: Int? = Int("42")
let longForm: Optional<Int> = Int("42")
Optional Type은 두 가지 case를 가진 Enumertaion입니다. nil을 의미하는 Optional.none과 wrapped value를 저장하고 있는 Optional.some이 있습니다.
let number: Int? = Optional.some(42)
let noNumber: Int? = Optional.none
print(noNumber == nil) // true
결론적으로, Optional 타입의 변수에는 nil이 할당될 수 있고 Non-Optional 타입의 변수에는 nil이 할당될 수 없습니다.
var serverResponseCode: Int = 404
serverResponseCode = nil // 불가능
var serverResponseCode: Int? = 404
serverResponseCode = nil // 가능
var surveyAnswer: String? // 자동으로 nil 세팅
Java를 경험해 본 적이 있다면 NullPointerException을 방지하기위한 방어로직을 본적이 있을 것입니다. Optional은 이런 Runtime Error를 Compile 단계에서 막을 수 있는 매우 유용한 기능입니다.
// Optional을 사용하지 않는다면 아래와 같이 처리해야함
if convertedNumber != nil {
print("convertedNumber contains some integer value.")
}
이제, Optional을 활용해보도록 하겠습니다.
Optional은 값을 감싸고 있는 상자라고 생각하면 편합니다.
상자안에 있는 값을 꺼내기 위한 방법은 2가지가 있습니다.
- 상자안에 값이 있는지 확인한 후에 꺼내는 방법
- 강제로 상자를 뜯어서 꺼내는 방법
활용 방법을 소개하기 전 예시에 필요한 이미지에 대한 정보가 담겨있는 Dictionary를 하나 선언하도록 하겠습니다.
let imagePaths = ["star": "/glyphs/star.png",
"portrait": "/images/content/portrait.jpg",
"spacer": "/images/shared/spacer.gif"]
Optional Binding
앱이 죽지 않도록 안전하게 값을 꺼내서 사용해보도록 하겠습니다. Optional Binding에는 if-let, guard-let, switch를 활용할 수 있습니다.
// if-let 구문
if let starPath = imagePaths["star"] {
print("The star image is at '\(starPath)'")
} else {
print("Couldn't find the star image")
}
// guard-let 구문
guard let starPath = imagePaths["star"] else { return }
print("The star image is at '\(starPath)'")
// 모두 "The star image is at '/glyphs/star.png'" 가 출력됨
Optional Chaining
wrapped instance의 프로퍼티나 메소드에 안전하게 접근하기 위해서 optional chaining을 활용할 수 있습니다.
if imagePaths["star"]?.hasSuffix(".png") == true {
print("The star image is in PNG format")
}
// "The star image is in PNG format"가 출력됨
// imagePaths["star"]가 nil이었다면 바로 비교를 종료함
Nil-Coalescing Operator
해당 변수가 nil일 경우 nil-coalescing operator(??)를 사용하여 default value를 지정해줄 수 있습니다.
let defaultImagePath = "/images/default.png"
let heartPath = imagePaths["heart"] ?? defaultImagePath
print(heartPath) // nil이므로 기본값으로 지정한 "/images/default.png"이 출력
Unconditional Unwrapping
Optional 타입의 변수에 nil이 절대 할당되지 않을 것이란 확신이 있을 경우에는 상자를 강제로 뜯는 forced unwrapping을 사용합니다. 변수뒤에 느낌표를 붙여서 사용하면 됩니다.
let number = Int("42")!
print(number) // 42
let isPNG = imagePaths["star"]!.hasSuffix(".png")
print(isPNG) // true
강제로 뜯어서 사용하는 만큼 편하지만 매우 위험한 방법입니다. 만약 해당 변수에 nil이 할당되어 있을 때 forced unwrapping을 사용할 경우 Runtime Error가 발생하여 앱이 강제 종료 되기 때문입니다.
선언단계에서 implicitly unwrapped으로 선언할 수도 있습니다.
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 느낌표가 필요
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 느낌표가 필요하지 않음
정리
- Optional Type은 2가지의 case를 가지는 Generic Enumeration 입니다.
- Optional Value를 사용할 때는 다양한 방법으로 안전하게 꺼내어 사용해야 합니다.
- Forced Unwrapping(!)을 사용할 경우 Runtime Error의 위험이 존재합니다.
'Swift' 카테고리의 다른 글
[Swift] Type Casting (is, as, as?, as!) 타입캐스팅 완벽 정리 (0) | 2019.12.15 |
---|---|
[Swift] Escaping Closure 탈출 하는 클로저!? (0) | 2019.12.15 |
[Swift] Capturing Values 값을 캡쳐한다는 것 (0) | 2019.12.15 |
[Swift] Closure? 함수 블록 클로저에 대해 알아보자 (0) | 2019.12.15 |
[Swift] Result Type으로 명확한 결과값 만들기 (0) | 2019.12.14 |