Optional Type
옵셔널 타입이란 값이 있을 수도 있고, 없을 수도 있는 타입을 말한다.
Swift에서는 값이 없음을 표시하기 위해 nil을 사용한다.
참조가 없음을 의미하는 다른 언어의 nil과는 사뭇 다르다.
var message: String = "My String"
message = nil // Error
Swift의 타입은 기본적으로 Non-Optional 타입이다. 즉, 반드시 값이 있어야 한다.
이러한 타입에 nil을 할당하면 컴파일 에러가 발생한다.
var myString1: String?
var myString2: Optional<String>
이것이 옵셔널 타입을 선언하는 방식이다.
위 두 변수는 이름만 다르고 같은 변수이다.
옵셔널 타입의 변수의 초깃값은 기본적으로 nil로 설정된다.
enum Optional<T> {
case None
case Some(T)
}
옵셔널 타입은 내부적으로 Enumertaion으로 정의한다.
우리가 어떤 변수를 옵셔널 타입을 선언할 때,
해당 변수에 nil을 할당했다면, None 값을 갖는 것이다.
해당 변수에 특정 값을 할당했다면, <T>에 대응하는 associated value를 갖게 되는 것이다.
Optional Type의 필요성
옵셔널은 런타임 오류를 방지하는 데에 그 목적이 있다.
옵셔널을 사용하면 컴파일 단계에서 변수 초기화 문제를 발견할 수 있다.
int i;
MyObject *m;
-(int)myMethodWithValue:(int)i {
return i*2;
}
NSLog(@"Value: %d",[m myMethodWithValue:5]);
Objective-C에 이런 코드가 있다.
i와 m을 선언하고, 초기화는 하지 않는다고 가정하자.
이 코드를 실행하면 의도한 10(5 * 2)의 값이 아니라, 0의 값을 얻게 된다.
왜 why?
옵젝씨에서는 값을 초기화하지 않은 정수형 변수를 0으로 초기화하나 보다.
중요한 것은 에러조차 없이 이상하게 동작한다는 것이다.
이런 문제는 찾아내기 굉장히 어렵다.
var myString: String
print(myString) // ERROR: Variable 'myString' used before being initialized
같은 상황에서 Swift는 컴파일 에러를 띄워준다.
값이 필요하면 반드시 초기화를 하고,
값이 없을 수도 있는 경우에는 무조건 Optional 변수를 사용하라는 뜻이다. 참 따뜻하다.
Optional Unwrapping
옵셔널 타입의 변수를 사용하기 전에는 반드시 값이 있는지 없는지 검증해야 한다.
nil이 할당되어 있는 변수를 사용하려다가는 런타임 에러가 발생하면서 앱이 깨지고 만다. 와장창.
옵셔널 변수의 값을 얻어내는 것을 Optional Unwrapping이라고 한다.
두 가지 방법을 제시한다.
Forced unwrapping
// #1
var myString1: String?
myString1 = "test"
var test: String = myString1!
print(myString1) // test
// #2
var myString1: String?
myString1 = nil
var test: String = myString1! // Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
print(test)
옵셔널 변수의 끝에 느낌표(!)를 붙이면, 안에 있는 값을 강제로 얻을 수 있다.
프로그래밍에서 강제란... 보통은 하면 안 되는 것이다.
Swift에서도 같다.
웬만한 경우에는 Force unwrapping을 사용하지 않을 것을 권장한다.
#1번의 경우에는 정상적으로 unwrapping이 된다.
#2번의 경우에는 컴파일러가 myString1에는 값이 있을 것이라고 굳게 믿고 코드를 실행한다.
아뿔싸. 값이 없다. 런타임 에러를 만나고 앱이 깨진다.
Optional Binding
// #1
if let constantName = optionalVariable {
statements
}
// #2
if var variableName = optional {
statements
}
보다 평화롭고 권장되는 방법이다.
등호를 기준으로 오른쪽에 있는 옵셔널 변수의 값을 확인한다.
nil이 아닌 값이라면, constantName 변수에 할당한다.
if statement 범위에서 constantName 변수를 자유롭게 사용할 수 있다.
세간의 인식과는 다르게 `if let` 뿐만 아니라 `if var`도 가능하다.
var myString3: String?
myString3 = "Space, the final frontier"
if let tempVar = myString3 {
print(tempVar) // "Space, the final frontier"
} else {
print("No value")
}
print(tempVar) // Compile Error: Cannot find 'tempVar' in scope
실전 예시를 보면 이렇다.
함수 내부에서 tempVar는 잘 사용할 수 있다.
함수를 벗어나서 사용하려고 하면, 컴파일 에러가 발생한다. 당연하다.
if let tmp1 = optional1, let tmp2 = optional2, let tmp3 = optional3 {
print(tmp1)
print(tmp2)
...
}
위와 같이 하나의 optional binding line에 여러 개의 옵셔널 변수를 unwrapping하는 것도 가능하다.
이때 하나의 변수라도 nil이면, 모든 unwrapping에 실패한다.
if let myOptional = myOptional {
print(myOptional)
} else {
print("myOptional was nil")
}
같은 이름의 변수에 unwrapping 결과물을 할당하는 것도 가능하다.
변수 이름 작명에 고심하는 개발자를 배려하는 따뜻한 조처이다.
Optional types with tuples
var tuple1: (one: String, two: Int)?
var tuple2: (one: String, two: Int?)
튜플을 통째로 옵셔널로 만들 수 있다.
튜플의 원소 하나만을 옵셔널로 만들 수도 있다.
Optional Chaining
var tireSize = car?.tires?.tireSize
프로퍼티, 메서드, subscript를 호출할 때, 그것의 return 값이 optional인 경우 위와 같이 사용할 수 있다.
chaining된 값 중에서 하나라도 nil이면, 전체 변수가 nil이 된다.
모든 값이 nil이 아니라면, 전체 변수는 optional unwrapping된 값을 얻는다.
The nil coalescing operator
optionalA ?? defaultValue
병합 연산자라고들 하는 것 같다. 발음은 코얼레싱에 가깝다.
삼항 연산자(tenary operator)와 유사한 방식으로 동작한다.
좌항의 값이 nil이라면 defaultValue가 할당된다.
nil이 아니라면 optional unwrapping된 값이 할당된다.
varr defaultName = "Jon"
var optionalA: String?
var optionalB: String?
optionalB = "Buddy"
var nameA = optionalA ?? defaultName // "Jon"
var nameB = optionalB ?? defaultName // "Buddy"
어렵지 않다.
optionalA는 nil이니까 defaultName.
optionalB는 nil이 아니니까 "Buddy"
var nameC1 = optionalA ?? defaultName
var nameC2 = optionalA != nil ? optionalA! : defaultName
위 두 문장은 같은 코드이다.
coalescing operator를 적극 활용하자.
참고
<Mastering Swift 5.3>, Jon Hoffman, 6th Edition
Chapter 3: Learning about Variables, Constants, Strings, and Operators
'Dev > Swift' 카테고리의 다른 글
[Swift] 타입(Type)의 종류 (0) | 2024.02.04 |
---|---|
[Swift] class와 struct의 차이 (메모리를 중심으로) (2) | 2024.01.25 |
Swift 문법 (3) - Tuple, Enumeration, Operators (1) | 2024.01.14 |
Swift 문법 (2) - Variables, Constants, Strings, Types (1) | 2024.01.14 |
Swift 문법 (1) - Swift 문법 특징 (0) | 2024.01.11 |