옵셔널
옵셔널은 스위프트의 특징 중 하나인 안정성을 담보하는 기법이다.
옵셔널은 말 그대로 “있을 수도?,“없을 수도?”를 나타내는 표현이다. 즉 변수(var) 또는 상수(let)의 값이 nil(NULL)일 수 있다는 것이다.
(+ 옵셔널 변수/상수가 아니면 nil을 할당할 수 없다.)
즉, 변수 혹은 상수가 nil일 수 있으니 사용에 주의하라는 의미를 담고 있는 것이다.
nil을 할당하기 위해서는 “?”를 데이터 타입 뒤에 붙여줘야 한다.
var printName: String = "cha"
printName = nil
// nil cannot be assigned to type "String"
위 코드처럼 “?”를 붙이지 않으면 오류가 발생하게 된다.
var printName: String? = "cha"
printName = nil
// nil
앞의 코드에서 데이터 타입 뒤에 “?”를 붙여야 nil을 할당할 수 있게 된다.
조금 더 자세하게 쓰자면 아래와 같이 쓸 수 있다.
var name: Optional<String>
다만 “?”를 붙이는 것이 위의 방법보다 간단하고 직관적이다.
만약 매개변수가 옵셔널일 때는, 해당 매개변수에는 값이 없어도 된다는 것을 간파할 수 있어야 한다.
옵셔널 열거형의 정의를 보면, 값을 갖는 케이스와 아닌 케이스 두 가지로 나뉘어있다.
값을 가질 경우 연관값이 존재하는데, 이 때 옵셔널에 값이 있으면 해당 연관값에 할당이 된다. 즉 옵셔널이라는 열거형의 방패막에 보호되어 싸여있다는 것이다. 이 때 옵셔널의 값을 추출하는 법을 알아두어야 한다.
옵셔널 추출
열거형의 some 케이스로 숨어있는 옵셔널의 값을 옵셔널이 아닌 값으로 추출하는 옵셔널 추출(optional unwrapping) 방법에 대해 알아보자.
강제 추출(Forced Unwrapping)
해당 방식은 값을 추출하기 위한 가장 간단한 방법이지만, 그 만큼 가장 위험한 방법이다. (런타임 오류가 일어날 수 있기 때문이다)
해당 방법으로 값을 추출하려면 옵셔널 값 뒤에 “!”를 붙여주면 된다. 강제 추출 시 옵셔널에 값이 없다면, 즉 nil이라면 런타임 오류를 발생시킨다.var soccer: String? = "Messi"
var barcelona: String = soccer!
soccer = nil
barcelona = soccer!
// Execution was interrupted
런타임 오류의 가능성을 항상 갖고있기에, 강제 추출 방식은 지양해야한다.
옵셔널 바인딩(Optional Binding)
옵셔널 바인딩은 조금 더 세련된 방식에 해당된다. 옵셔널 바인딩은 옵셔널에 값이 있는지 확인할 때 사용하며, 값이 있다면 옵셔널에서 추출한 값을 일정 블록 안에서 사용할 수 있는 var이나 let으로 할당하여 옵셔널이 아닌 상태로 사용 가능하게 해준다. 옵셔널 바인딩은 if나 while 구문 등과 결합하여 사용 가능하다.
if soccer != nil {
print("\(soccer) is the best")
} else {
print("soccer == nil")
}
// nil일 경우 else문 실행
var realLegend: String? = "guti"
// 임시 상수 할당
if let name = realLegend {
print("\(name) is the legend")
} else {
print("--nil--")
}
// guti is the legend
// 임시 변수 할당
if var name = realLegend {
name = "Kroos"
print("\(name) is the legend")
} else {
print("--nil--")
}
// Kroos is the legend
블록 안쪽에서만 name이라는 임시 상수를 사용할 수 있다. 그렇기에 모두 별도로 name을 사용하였지만, 충돌하지 않았다. 또한 상수로 사용하지 않고 var을 통해 임시 변수로 할당해도 된다.
또한 옵셔널 바인딩을 통해 한 번에 여러 옵셔널의 값을 추출할 수 있다. 쉼표를 사용하여 바인딩 할 옵셔널의 값을 나열하면 된다. 다만 하나라도 값이 없다면 해당 블록 내부의 명령문은 실행되지 않는다!var arsenal: String? = "walcott"
var chelsea: String? = nil
var everton: String? = "arteta"
if let london1 = arsenal, let london2 = chelsea {
print("\(london1) 과 \(london2)는 프리미어리그 선수였다.")
}
// 반환값 없음
if let london1 = arsenal, let london2 = everton {
print("\(london1) 과 \(london2)는 프리미어리그 선수였다.")
}
// walcott과 arteta는 프리미어리그 선수였다.
옵셔널 바인딩은 옵셔널 체이닝과 환상의 짝꿍이다. 이후 챕터에서 알아보자.
암시적 추출 옵셔널
암시적 추출 옵셔널로 지정하고 싶다면 ?가 아닌 !를 타입 뒤에 붙여주면 된다. 이는 일반값처럼 사용할 수 있으나, 여전히 옵셔널인 것은 변함이 없기에 nil값을 할당해 줄 수 있다. 다만 nil값이 할당되어 있다면, 런타임 오류가 발생한다.
옵셔널을 사용할 때에는 강제 추출, 암시적 추출 옵셔널보다는 옵셔널 바인딩과 체이닝을 사용하는 것이 가장 바람직하고 안전하다!