지금까지 알아본 옵셔널은 사용전에 옵셔널 바인딩이나 nil 병합 연산을 통해 언래핑을 해주어야했다.
옵셔널 중에는 옵셔널이지만 언래핑하지 않고도 사용할 수 있는 옵셔널이 있다. 바로 암시적으로 언래핑한 옵셔널이다.
암시적으로 언래핑한 옵셔널의 특성 및 사용 방법을 우선 알아본 다음 언제 사용하는지에 대해 알아보자.
우선 암시적으로 언래핑한 옵셔널은 다음과 같이 선언할 수 있다.
var age: Int! = nil // 암시적으로 언래핑한 옵셔널
var name: String? = nil // 일반 옵셔널
보통 옵셔널 타입의 변수를 선언할 때 타입 뒤에 물음표를 붙였다면, 암시적으로 언래핑한 옵셔널은 뒤에 느낌표를 붙여서 선언한다.
암시적으로 언래핑한 옵셔널 또한 옵셔널이기 때문에 값의 부재를 나타내는 nil
을 할당할 수도 있고 값을 할당할 수도 있다.
var age: Int! = nil
age = 10
age = nil
그리고 암시적 옵셔널 또한 옵셔널이기 때문에 옵셔널 바인딩이나 nil 병합 연산을 통해 값이 있을 때와 없을 때 처리를 분기해서 사용할 수 있다.
// 옵셔널 바인딩과 암시적으로 언래핑한 옵셔널을 사용한 옵셔널 처리
var age: Int! = nil
guard let age = age else { print("age not found") }
print("age found: \(age):)
// nil 병합 연산과 암시적으로 언래핑한 옵셔널을 사용한 옵셔널 처리
var age: Int! = 10
print(age ?? "age not found")
var newAge = (age ?? 0) + 2
print(newAge) // 12
지금까지는 보통 옵셔널과는 선언시에 타입 뒤에 느낌표를 붙인다는 것을 제외하고는 일반 옵셔널과 다른점이 없다.
일반 옵셔널과 암시적으로 언래핑한 옵셔널은 값에 접근하여 사용하는 방식에서 차이가 있다.
앞서 말했듯이 암시적으로 언래핑한 옵셔널은 옵셔널 언래핑 처리를 해주지 않아도 사용할 수 있다. 암시적으로 언래핑한 옵셔널은 값에 접근할 때마다 옵셔널 값을 강제 언래핑을 하는 것과 같다. 때문에 좀전에 옵셔널 바인딩이나 nil 병합 연산을 하지 않아도 옵셔널이 벗겨진 상태로 사용이 가능하다.
var age: Int! = 10
print(age + 10) // 20
여기서 age는 암시적으로 언래핑한 옵셔널이다. 일반적인 다른 옵셔널들은 사용 전에 꼭 옵셔널 언래핑 처리를 해주어야 옵셔널으로 감싸진 값을 사용할 수 있었다. 그런데 암시적으로 언래핑한 옵셔널은 옵셔널 언래핑 처리를 하지 않아도 일반 값처럼 사용이 가능하다.
위의 age는 다음과 같이 작동하는 것과 동일하다고 판단할 수 있다.
var age: Int? = 10
print(age! + 10) // 20
여기서 중요한 점은 age가 nil
일때도 똑같이 동작한다는 것이다.
// 암시적으로 언래핑한 옵셔널 선언
var age: Int! = nil
print(age + 10)
---------------
// 일반 옵셔널 선언 + 강제 언래핑
var age: Int? = nil
print(age! + 10)
강제 언래핑에서 보았듯이, nil
이 담긴 옵셔널을 강제 언래핑하면 프로그램이 비정상적으로 종료된다. 그렇기 때문에 nil
이 담겨있는 암시적으로 언래핑한 옵셔널을 사용하면, nil
이 담긴 옵셔널을 강제 언래핑하는 것과 마찬가지기 때문에 runtime에 프로그램이 비정상적으로 종료된다.
암시적으로 언래핑한 옵셔널의 특징은 다음과 같이 정리할 수 있다.
nil
혹은 값 할당이 가능하다)nil
이 들어있는 암시적으로 언래핑한 옵셔널에 접근하면 runtime에 프로그램이 비정상적으로 종료된다.지금까지 암시적으로 언래핑한 옵셔널에 특징에 대해서 알아보았는데, 그래서 언제 사용하는 것이 적절할까?
스위프트 공식 문서에는 암시적으로 언래핑한 옵셔널에 대해 이렇게 적혀있다.
Sometimes it’s clear from a program’s structure that an optional will always have a value, after that value is first set. In these cases, it’s useful to remove the need to check and unwrap the optional’s value every time it’s accessed, because it can be safely assumed to have a value all of the time.
때때로 프로그램의 구조상 어떤 특정한 시점에는 옵셔널에 값이 무조건 들어있다는 것을 명확하게 알 수 있다. 이러한 경우에는 옵셔널에 값에 접근할때마다 매번 옵셔널 언래핑 처리를 해주지 않아도 되는 것이 해당 사용성을 증가 시킬 수도 있다. 왜냐하면 해당 옵셔널에는 값이 항상 있다고 추측할 수 있기 때문이다.
옵셔널에 접근할 때마다 옵셔널 언래핑을 하면 코드가 길어지고 가독성이 떨어질 수 있다. 그렇기 때문에 프로그램의 구조상 특정 시점 이후로 옵셔널에 값이 항상 있다고 추측이 가능하면 암시적으로 언래핑한 옵셔널으로 선언하여 옵셔널 언래핑 처리 과정을 생략하여 가독성과 편리성을 높일 수 있다.
그렇지만 암시적으로 언래핑한 옵셔널은 nil
이 담겨 있을 경우에 프로그램이 종료되기 때문에 위험하다는 의견도 있다. 프로그램 구조에 대해 이해도가 높은 개발자이 암시적으로 언래핑으로 옵셔널으로 선언할 때는 프로그램이 종료되는 상황을 만들지 않겠지만, 만약 새로 들어온 개발자가 추후에 해당 프로그램 개발 및 유지보수를 맡게 된다면 프로그램 구조에 대한 이해도가 낮아서 기존에 암시적으로 언래핑한 옵셔널에서 프로그램이 종료될 수도 있는 상황이 생길 가능성도 있다.
애플이 사용하는 암시적으로 언래핑한 옵셔널에 대한 대표적인 예시로는 IBOutlet을 꼽을 수 있다. 스토리보드에서 뷰객체가 생성되기 전에는 IBOutlet은 nil이 들어있다. 하지만 뷰 객체가 생성되고 난 뒤로부터는 해당 뷰컨트롤러에 항상 값이 들어있을 것이기 때문에 암시적으로 언래핑한 옵셔널으로 선언되어 있다.
import UIKit
class ViewController: UIViewController {
@IBOutlet var button: UIButton!
}
옵셔널에 대한 7가지 개념
force unwrapping ✅
optional binding - if ✅
optional binding - guard ✅
nil coalescing ✅
optional chaining ✅
implicitly unwrapped optional ✅
optional pattern