옵셔널(Optional)
Swift가 가진 가장 큰 특징 중 하나가 바로 옵셔널(Optional)이다. 이 옵셔널은 값이 있을 수도 있고 없을 수도 있다는 것을 나타낸다.
예를 들어 문자열의 값이 있으면 "가나다"가 되지만, 값이 없다면 ""가 될까? 답은 아니다. ""도 엄연히 값이 있는 문자열이라고 할 수 있다. 값이 없는 문자열을 nil 이라고 한다.
또 다른 예를 들어 정수형의 값이 있으면 100과 같은 값이 존재할 것이다. 값이 없다면 0이 될까? 0은 0이라는 숫자 '값'이기 때문에 값이 없는 정수 또한 nil 이다.
하지만 모든 변수에 nil을 넣을 수 있는 것은 아니다.
var name: String = "Soo"
name = nil
'nil' cannot be assigned to type 'String'
var email: String?
print(email) // nil
email = "abcd@gmail.com"
print(email) // Optional("abcd@gmail.com")
값이 있을 수도 있고 없을 수도 있는 변수를 정의할 때는 타입 어노테이션에 ?를 붙여야 한다. 이렇게 정의한 변수를 옵셔널(Optional)이라고 하며 옵셔널에 초기값을 지정하지 않으면 기본값은 nil이 된다.
let optionalEmail: String? = "abcd@gmail.com"
let requiredEmail: String = optionalEmail // Compile Error!
Value of optional type 'String?' must be unwrapped to a vlaue of type 'String'
옵셔널로 정의한 변수는 옵셔널이 아닌 변수와 다르기 때문에 위처럼 사용할 수 없다. requiredEmail 변수는 옵셔널이 아닌 String 타입이기 때문에 항상 값을 가지고 있어야 한다. optionalEmail은 옵셔널 변수이기 때문에 실제 코드가 실행되기 전까지는 값이 있는지 없는지 알 수 없다.
따라서 Swift 컴파일러는 안전을 위해 requiredEmail에는 옵셔널로 선언된 변수를 대입할 수 없게 만들었다.
옵셔널 바인딩(Optional Binding)
옵셔널의 값을 가져오고 싶을 경우에 사용하는 것이 옵셔널 바인딩이라고 한다.
옵셔널 바인딩은 옵셔널의 값이 존재하는지 검사한 뒤, 값이 존재한다면 그 값을 다른 변수에 대입시키는 역할을 한다. if let 또는 if var를 사용하는데 옵셔널의 값을 벗겼을 때 값이 있다면 if문 안으로 들어가고 값이 nil이라면 통과한다.
if let email = optionalEmail {
print(email) // optionalEmail의 값이 존재할 경우 해당 값 출력
}
// optionalEmail 값이 존재하지 않을 경우 if문 통과
하나의 if문에서 콤마( , )로 구분하여 여러 옵셔널을 바인딩 할 수 있고, 모든 옵셔널의 값이 존재해야 if문 안으로 들어간다.
var optionalName: String? = "Soo"
var optionalEmail: String? = "abcd@gmail.com"
if let name = optionalName, let email = optionalEmail {
// name과 email의 값이 존재해야 들어온다.
}
깃북에서는 let name = ... , email 로 작성되어 있는데 Expected 'let' in conditional 에러가 발생한다.
각 optional에 대해 let을 사용하면 에러가 발생하지 않았다.
코드가 너무 길 경우에는 아래와 같이 여러 줄에 걸쳐서 작성할 수 있으며, 두 번째 let부터는 생략이 가능하다.
if let name = optionalName,
let email = optionalEmail {
// name과 email의 값이 존재해야 들어온다.
}
위 코드는 아래 코드와도 동일하다.
if let name = optionalName {
if let email = optionalEmail {
// name과 email의 값이 존재해야 들어온다.
}
}
한 번의 if문에서 여러 옵셔널을 바인딩할 수 있게 된 것은 Swift 1.2 버전부터이다. 이전 버전까지는 바로 위와 같이 여러 번으로 감싸진 옵셔널 바인딩을 사용했다.
옵셔널을 바인딩할 때 ( , )를 사용해서 조건도 함께 지정할 수 있다. 콤마 이후의 조건절은 옵셔널 바인딩이 일어난 후에 실행되며 즉, 옵셔널이 벗겨진 값을 가지고 조건을 검사하게 된다.
var optionalAge: Int? = 20
if let age = optionalAge, age >= 20 {
// age 값이 존재하고 age가 20 이상일 경우 진입
}
// 또는
if let age = optionalAge {
if age >= 20 {
// age 값이 존재하고 age가 20 이상일 경우 진입
}
}
옵셔널 체이닝(Optional Chaining)
옵셔널로 선언한 어떤 배열이 있을 때, 이 배열이 빈 배열인지 아닌지 검사하려면 어떻게 해야할까?
let array: [String]? = []
var isEmptyArray = false
if let array = array, array.isEmpty {
isEmptyArray = true
} else {
isEmptyArray = false
}
isEmptyArray
nil이 아니면서 빈 배열인지 아닌지를 확인해보면 되는데 위 코드를 옵셔널 체이닝을 사용하면 간결하게 쓸 수 있다.
let array: [String]? = []
let isEmptyArray = array?.isEmpty == true
옵셔널 체이닝은 옵셔널의 속성에 접근할 때, 옵셔널 바인딩 과정을 ? 키워드로 줄여주는 역할을 한다.
array?.isEmpty의 결과로 나올 수 있는 값은 nil, true, false 3개이다. isEmpty의 반환값은 Bool인데 옵셔널 체이닝으로 인해 Bool?을 반환하도록 바뀐 것이다. 따라서 값이 실제로 true인지 확인하려면 == true를 해주어야 한다.
옵셔널 벗기기
옵셔널을 사용할 때마다 옵셔널 바인딩을 하는 것이 제일 좋지만 개발을 하다보면 값이 분명히 존재할 것임에도 불구하고 옵셔널로 사용해야 하는 불편함이 있을 수 있다. 이런 경우에는 옵셔널에 값이 있다고 가정하고 바로 접근할 수 있도록 ! 키워드를 붙이면 된다.
print(optionalEmail) // Optional("abcd@gmail.com")
print(optionalEmail!) // abcd@gamil.com
! 를 사용할 때 주의할 점은, 옵셔널의 값이 nil인 경우에는 런타임 에러가 발생한다. Java의 NullPointerException과 비슷하다고 볼 수 있다.
var optionalEmail2: String?
print(optionalEmail2!)
fatal error: Unexpectedly found nil while unwrapping an Optional value
🐣 출처: 전수열님 GitBook
이 글은 전수열님의 깃북을 참고하여 작성한 글입니다.