안전성
을 문법으로 담보하는 기능변수, 상수에 정말 값이 없을 때만 nil로 표현합니다.
(0
이나 ""
도 하나의 값으로, 값이 없는 것은 아님)
함수형 프로그래밍 패러다임에서 자주 등장하는 모나드 개념과 일맥상통합니다.
값이 없는 옵셔널 변수, 상수에 강제로 접근하려면 런타임 오류가 발생합니다.
nil은 옵셔널로 선언된 곳에서만 사용할 수 있습니다
옵셔널은 데이터 타입 뒤에 물음표 ?
를 붙여 표현해줍니다.
enum school: String {
case primary = "유치원"
case elementary = "초등학교"
case middle = "중학교"
case high = "고등학교"
case college = "대학"
case university = "대학교"
case grauate = "대학원"
}
let primary = School(rawValue: "유치원") // primary
let graduate = School(rawValue: "석박사") // nil
데이터 타입을 명시하지 않고 타입 추론 기능을 사용한 예제입니다.
컴파일러는 primary, graduate의 데이터 타입을 School? 이라고 추론했을 것입니다.
이 때 원시 값이 열거형의 case에 해당하지 않으면 열거형 인스턴스 생성에 실패하여 nil을 반환하는 경우가 생깁니다.
옵셔널은 열거형으로 정의되어있습니다.
public enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
public init(_ some: Wrapped)
/// 중략 ...
}
옵셔널은 값을 갖는 케이스와 그렇지 못한 케이스 두가지로 정의되어 있습니다.
none
케이스some
케이스, 연관값으로 Wrapped
따라서 옵셔널에 값이 있으면 some
의 연관값인 Wrapped
에 값이 할당됩니다.
즉 값이 옵셔널이라는 열거형의 방패막에 보호되어 래핑되어 있는 모습입니다.
옵셔널 자체가 열거형이기 때문에 옵셔널 변수는 switch 구문을 통해 값이 있고 없음을 확인할 수 있습니다.
func checkOptionalValue(value optionalValue: Any?) {
switch optionalValue {
case .none:
print("This optional variable is nil")
case .some(let value):
print("Value is \(value)")
}
}
var myName: String? = "toma"
checkOptionalValue(value: myName) // Value is toma
myName = nil
checkOptionalValue(value: myName) // This optional variable is nil
여러 케이스의 조건을 통해 검사하고자 한다면 where 절과 병합해서 쓰면 좋습니다.
let numbers: [Int?] = [2, nil, -4, nil, 100]
for number in numbers {
switch number {
case .some(let value) where value < 0:
print("Negative value!! \(value)")
case .some(let value) where value > 10:
print("Large value!! \(value)")
case .some(let value):
print("Value \(value)")
}
case .none:
print("nil")
}
// Value 2
// nil
// Negative value!! -4
// Large value!! 100
그러나 단 하나의 옵셔널을 switch 구문을 통해 값이 있는지 확인하는 것을 불편하기에, 값을 좀 더 안전하고 편리하게 추출하는 방법에 대해 알아보겠습니다.
열거형의 some 케이스로 꼭꼭 숨어있는 옵셔널의 값을 옵셔널이 아닌 값으로 추출하는 여러가지 옵셔널 추출 방법들이 있습니다.
옵셔널 강제 추출 방식은 가장 간단하지만 가장 위험합 방법입니다.
런타임 오류가 일어날 가능성이 가장 높기 때문입니다.
옵셔널의 값을 강제 추출하려면 옵셔널 값의 뒤에 느낌표 !
를 붙여주면 됩니다.
만약 강제 추출 시 옵셔널에 값이 없다면, 런타임 오류가 발생합니다.
옵셔널이 아닌 변수애는 옵셔널 값이 들어갈 수 없어서 추출해서 할당해주어야 합니다.
nil을 강제 추출했기 때문에 런타임 오류가 발생합니다.
if 구문 등 조건문을 이용해서 조금 더 안전하게 강제 추출하는 방법입니다.
if 구문을 통해 값이 nil인지 먼저 확이나혹 옵셔널 값을 강제 추출하는 방법은 옵셔널 사용 이유가 무의미합니다.
스위프트는 조금 더 안전하고 세련된 방법으로 옵셔널 바인딩
을 제공합니다.
만약 옵셔널에 값이 있다면 옵셔널에서 추출한 값을 일정 블록 안에서 사용할 수 있는 상수나 변수로 할당해서 옵셔널이 아닌 형태로 사용할 수 있도록 해줍니다.
,
를 통해한번에 여러 옵셔널의 값을 추출할 수 있습니다.var myName: String? = "toma"
if let name = myName {
print("My name is \(name)")
} else {
print("myName == nil")
}
var yourName: String? = nil
// 옵셔널 바인딩을 사용해 여러 개의 옵셔널 값 추출
if let name = myName, let friend = yourName {
print("We are friend! \(name) & \(friend)")
}
// My name is toma
옵셔널 바인딩과 함께 사용되는 옵셔널 체이닝에 대한 내용은 다른 14장 옵셔널 체이닝과 빠른 종료에서 다룸
nil을 할당하고 싶지만, 옵셔널 바인딩으로 매번 값을 추출하기 귀찮거나 로직상 nil 때문에 런타임 오류가 발생하지 않는다는 확신이 들 때
nil을 할당해줄 수 있는 옵셔널이 아닌 변수, 상수에 사용하는 것이 암시적 추출 옵셔널
입니다.
!
를 사용하면 됩니다.var myName: String! = "toma"
print(myName) // Optional(toma)
myName = nil
// 암시적 추출 옵셔널도 옵셔널이므로 바인딩을 사용할 수 있습니다.
if let name = myName { // myName == nil
print("My name is \(name)")
} else {
print("myName == nil")
}
myName.isEmpty // 오류
이렇게 그냥 옵셔널 값이든, 암시적 추출 옵셔널 값이든 옵셔널인건 똑같지만,
암시적 추출 옵셔널 값은 추출이 필요없다는 사실을 알 수 있습니다!