Swift에서 옵셔널은 있을 수도 있고 없을 수도(nil) 있는 값을 Optional 로 래핑(싸서) 해서 안전한 프로그래밍을 할 수 있는 방법을 제공합니다.
즉 , 해당 변수의 값이 있을 수도 있고 없을 수도 있는 상태를 나타내며, 두가지 방법으로 옵셔널을 표현 할 수 있습니다. 변수의 유형이 Int? 인 경우와 Optional<Int> 은 같은 표현 방법입니다. 일반적으로 코드를 읽고 쓰기 쉽도록 단축 형식(Int?)을 선호됩니다.
다음 코드 샘플에서 shortForm 및 longForm 유형은 동일합니다.
let shortForm: Int? = Int("42")
let longForm: Optional<Int> = Int("42")
옵셔널 타입은 두 가지 케이스가 있는 열거형입니다. Optional.none은 nil 과 동일합니다. Optional.some (Wrapped)은 래핑 된 값을 저장합니다. 예를 들면 아래와 같이 표현할 수 있습니다.
let number: Int? = Optional.some(42)
let noNumber: Int? = Optional.none
print(noNumber == nil)
// Prints "true"
여러 컨텍스트에서 사용하려면 먼저 Optional 인스턴스의 값을 언래핑 해야합니다. Swift는 옵셔널 값을 안전하게 언 래핑하는 여러 가지 방법을 제공하기 때문에 명확하고 간결한 코드를 작성하는 데 도움이되는 방법을 선택할 수 있습니다.
let imagePaths = ["star": "/glyphs/star.png",
"portrait": "/images/content/portrait.jpg",
"spacer": "/images/shared/spacer.gif"]
위의 코드에서 “키”를 사용하여 딕셔너리 값을 가져 오면 옵셔널 값이 반환되므로 imagePaths [ “star”]의 유형은 Optional 이거나 선호하는 방식으로 String?으로 작성 할 수 있습니다. 옵셔널 값을 안전하게 언랩핑 하는 방법은 아래의 옵셔널 바인딩으로 구현할 수 있습니다.
옵셔널 바인딩 (Optional Binding)
Optional 인스턴스의 래핑 된 값을 새 변수에 조건부로 바인딩하려면 if let, guard let 및 switch를 포함한 옵셔널 바인딩 방법 중 하나를 사용하면 됩니다.
if let starPath = imagePaths["star"] {
print("The star image is at '\(starPath)'")
} else {
print("Couldn't find the star image")
}
// Prints "The star image is at '/glyphs/star.png'"
옵셔널 체이닝 (Optional Chaining)
래핑 된 인스턴스의 프로퍼티와 메서드에 안전하게 액세스하려면 postfix 옵셔널 연결 연산자 (postfix?)를 사용하면 됩니다. 다음 예제에서는 옵셔널 체이닝을 사용하여 String?의 hasSuffix (_ 🙂 메서드에 액세스합니다.
아래의 코드에서 imagePaths[“star”]? 이 nil 이라면 hasSuffix 가 실행되기 전에 nil 을 반환 햐여 false가 될것입니다.
if imagePaths["star"]?.hasSuffix(".png") == true {
print("The star image is in PNG format")
}
// Prints "The star image is in PNG format"
Nil 통합 연산자 (Using the Nil-Coalescing Operator)
Optional 인스턴스가 nil 인 경우 기본값을 제공하려면 nil-coalescing 연산자 (??)를 사용합니다. 여기에는 imagePaths에서 누락 된 이미지에 대한 기본 경로가 제공됩니다.
let defaultImagePath = "/images/default.png"
let heartPath = imagePaths["heart"] ?? defaultImagePath
print(heartPath)
// Prints "/images/default.png"
?? 연산자는 오른쪽의 다른 Optional 인스턴스에서도 작동합니다.
(옵셔널값1 ?? 옵셔널값2 ?? 기본값 )
let shapePath = imagePaths["cir"] ?? imagePaths["squ"] ?? defaultImagePath
print(shapePath)
// Prints "/images/default.png"
무조건 적인 언래핑 (Unconditional Unwrapping)
Optional 인스턴스에 값이 포함되어 있다고 확신하면 강제 언래핑 연산자 ! 를 사용하여 값을 무조건 언래핑 할 수 있습니다. 예를 들어 실패 가능한 Int 이니셜 라이저의 결과는 아래 예제에서 무조건 언 래핑됩니다.
let number = Int("42")!
print(number)
// Prints "42"
옵셔널 체이닝에서도 !를 사용하여 강제 언래핑을 수행 할 수도 있습니다.
let isPNG = imagePaths["star"]!.hasSuffix(".png")
print(isPNG)
// Prints "true"
중요 : nil 인스턴스를 강제언래핑하면 런타임 에러가 발생할 것입니다.
암시 적으로 언 래핑 된 옵션널 (Implicitly Unwrapped Optionals)
위에서 설명한 것처럼 옵셔널은 상수 또는 변수가 “값 없음”을 가질 수 있음을 나타냅니다. 옵셔널은 if 문으로 확인하여 값이 있는지 확인할 수 있으며 옵셔널 바인딩을 사용하여 조건부로 언래핑하여 옵셔널 값이있는 경우 액세스 할 수 있습니다.
프로그램의 구조에서 옵셔널 값이 처음 설정된 후에는 항상 값이 있음이 분명합니다. 이러한 경우에는 항상 값을 가지고 있다고 안전하게 가정 할 수 있으므로 옵셔널 값에 액세스 할 때마다 선택 사항의 값을 확인하고 언래핑 할 필요를 제거하는 것이 유용합니다.
이러한 종류의 옵셔널은 암시 적으로 언 래핑 된 옵셔널로 정의됩니다. 옵셔널로 만들려는 타입 뒤에 물음표 (String?) 대신 느낌표 (String!)를 배치하여 암시 적으로 언래핑 된 옵셔널 타입을 작성합니다. 옵셔널을 사용할 때 옵셔널의 이름 뒤에 느낌표를 배치하는 대신 선언 할 때 옵셔널타입 뒤에 느낌표를 배치합니다.
암시 적으로 언래핑 된 옵셔널은 옵셔널이 처음 정의 된 직후에 옵셔널 값이 존재하는 것으로 확인 될 때 유용하며 그 이후 모든 지점에 존재한다고 확실히 가정 할 수 있습니다. Swift에서 암시 적으로 언래핑 된 옵셔널의 주된 사용은 [Unowned References] 및 [Implicitly Unwrapped Optional Properties]에 설명 된대로 클래스 초기화 중 사용할 수 있습니다.
암시적으로 언래핑 된 옵셔널은 실제 일반적인 옵셔널이지만, 액세스 할 때마다 옵셔널 값을 풀 필요없이 옵셔널이 아닌 값처럼 사용할 수도 있습니다. 다음 예제는 래핑 된 값에 명시 적 문자열로 액세스 할 때 옵셔널 문자열과 암시 적으로 래핑되지 않은 옵셔널 문자열 간의 동작 차이를 보여줍니다.
let possibleString: String? = "옵셔널 문자열"
let forcedString: String = possibleString! // 느낌표가 필요
let assumedString: String! = "암시적으로 언랩핑된 문자열"
let implicitString: String = assumedString // 느낌표가 필요없음
암시적 언래핑 된 옵셔널은 필요할 경우 강제 언래핑되는 옵셔널에 대한 권한을 부여하는 것으로 생각할 수 있습니다. 암시적 언래핑 된 옵셔널 값을 사용할 때 Swift는 먼저 일반 옵셔널 값으로 사용하려고합니다. 옵셔널로 사용할 수 없는 경우 Swift는 값을 강제로 언래핑합니다. 위의 코드에서, implicitString은 명시적이고 옵셔널이 아닌 String 타입을 가지고 있기 때문에, 옵셔널 값 assumeString은 implicitString에 값을 할당하기 전에 강제로 언래핑됩니다. 아래 코드에서 optionalString은 명시적인 타입이 없으므로 일반적인 옵셔널 타입 입니다.
let optionalString = assumedString
// optionalString의 유형은 "String?"입니다. 그리고 assumedString 문자열은 강제로 풀리지 않습니다.
암시적 언래핑 된 옵셔널이 nil이고 래핑 된 값에 액세스 하려고 하면 런타임 오류가 발생합니다. 결과는 값을 포함하지 않는 일반 옵셔널 뒤에 느낌표를 넣는 것과 똑같습니다.
if assumedString != nil {
print(assumedString!)
}
// Prints "암시적 언래핑된 옵셔널 문자열."
또한 옵셔널 바인딩과 함께 암시적으로 래핑되지 않은 옵셔널을 사용하여 단일 문에서 해당 값을 확인하고 언래핑 할 수 있습니다.
if let definiteString = assumedString {
print(definiteString)
}
// Prints "암시적으로 언래핑된 옵셔널 문자열."