옵셔널 타입은 값이 있을수도 있고 없을수도 있음을 의미한다. nil의 가능성을 명시적으로 표현한다. 옵셔널은 값을 명시적으로 Optional로 감싸거나 해당 타입의 변수를 Optional로 선언하는 두 가지 방법으로 나타낼 수 있다.
enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
}
let optionalVal: Optional<Int> = nil
let optionalVal : Int? = nil
print(Int("123")) // Optional(123)
문자열 "123"은 int값 123으로 변환 가능하지만 "hello"는 int값으로 변환이 불가능하다. 문자열 타입을 int 타입으로 변환하는 경우 값이 있을 수도 있지만, 없을 수도 있는 것이다. 그런 경우 옵셔널을 사용해야한다.
let possibleNum = "123"
let convertedNum: Int? = Int(possibleNum)
//옵셔널에 기본값을 지정하지 않으면 자동으로 nil으로 설정한다.
var answer: String?
//기존 변수처럼 사용불가 : 옵셔널과 일반 값은 다른 타입이므로 연산 불가
optionalValue = nil
optionalValue = optionalValue + 1
옵셔널 타입은 애초에 연산을 지원하지 않는다. 연산을 위해서는 옵셔널 객체를 해제해야 하는데 내부의 값을 추출하는 과정을 옵셔널 언래핑이라고 한다.
var optInt: Int? = 3
print("옵셔널 자체의 값 : \(optInt)") // 옵셔널 자체의 값 : Optional(3)
print("!로 강제 해제한 값 : \(optInt!)") // !로 강제해제한 값 : 3
그러므로 강제 해제 연산자를 사용할 때는 옵셔널 값이 nil인지 체크가 필요
var str = "123"
var intFromStr = Int(str)
if intFromStr != nil {
print("변환된 값은 \(intFromStr!)입니다")
} else {
print("값 변환에 실패하였습니다")
}
var str = "Swift"
if let intFromStr = Int(str) {
print("변환된 값은 \(intFromStr)입니다")
} else {
print("값 변환에 실패하였습니다")
}
딕셔너리에 키로 접근하면 그 결과값은 옵셔널 타입으로 반환된다. 딕셔너리에 키로 사용될 수 있는 값은 Hashable 프로토콜이 구현된 모든 자료형이다. 키 사용 제한이 거의 없으므로 존재하지 않는 키를 사용할 가능성이 있다. 그러므로 키에 값이 비어있거나 입력된 키가 아예 없다는 것을 표현하기 위해 nil을 반환해야 한다.
프로그램에서 옵셔널 값이 항상 값을 가질것이 분명한 순간이 있다. 이럴때는 옵셔널을 항상 체크할 필요가 없다. 이럴때 사용하는 것이 암시적 추출 옵셔널이다. 즉, 옵셔널을 선언 후 값을 가질 것이 분명할 때 사용한다. 이후에 Nil값을 가질 것이 분명한 값에는 암시적 추출 옵셔널을 사용하면 안된다.
var optionalVal: Int! = 1
switch optionalValue {
case .none:
print("nil")
case .some(let value):
print("Value is \(value)")
//기존 변수처럼 사용가능
optionalVal = optionalVal + 1
//nil 할당 가능
optionalVal = nil
optionalVal = optionalVal + 1 //잘못된 접근으로 인한 런타임 오류 발견
암시적 추출 옵셔널은 일반적인 옵셔널과 같은 옵셔널타입이지만 옵셔널 값을 매번 해제하지 않고도 비옵셔널값처럼 사용할 수 있다.
let possibleString: String? = "optional String"
let forcedString: String = possibleString! //옵셔널 강제 해제
var implicitlyUnwrappedOptionalString: String! = "World"
print(implicitlyUnwrappedOptionalString) // 해제 없이 바로 출력
옵셔널 체이닝은 Swift에서 옵셔널 값에 연속적으로 접근하는 과정을 안전하게 수행하기 위한 매커니즘이다. 체인처럼 옵셔널이 연결되어 있을때 유용하게 사용할 수 있다!! 옵셔널 체이닝은 ? 연산자를 사용하여 구현된다. 이 연산자를 사용하여 옵셔널 값이 nil이면 바로 nil을 반환하고 만약 체인 중간에 nil이 포함되어 있다면 그 이후의 속성이나 메서드에 접근하지 않고 바로 nil을 반환한다.
class Address {
var city: String?
init(city: String?) {
self.city = city
}
}
class Person {
var address: Address?
init(address: Address?) {
self.address = address
}
}
let ally: Person? = Person(address: Address(city: "Seoul"))
Address 클래스와, Person 클래스가 있다. ally라는 인스턴스는 옵셔널타입이다. 만약 person인스턴스의 주소의 도시가 궁금하다면 옵셔널 바인딩으로 다음과 같이 해결할 수 있다.
func personCity(man: Person?){
if let man = man {
if let address = man.address {
if let city = address.city {
print("주소의 도시는 \(city)이다")
} else {
print(" X ")
}
}
}
}
그러나 옵셔널 체이닝을 이용하면 다음과 같이 더 간단하게 구현할 수 있다.
func personCity(man: Person?){
if let personCity = man?.address?.city {
print("주소의 도시는 \(personCity)이다")
}
}
personCity(man: ally) //주소의 도시는 Seoul이다