Optional이란 있을 수도, 없을 수도(nil) 있는 것 이다. 다른 언어를 공부하면 NULL을 볼 수 있다. 다른 언어에서는 NullPointException 오류가 발생하면 프로그램이 멈춘다. 스위프트는 안정성이 특징인데, Optional이 안정성을 제공하는 기능이다. 왜냐하면 값이 nil이어도 프로그램이 진행되기 때문이다.
옵셔널은 상자를 생각하면 쉽다. 단순하게 nil을 할당할 수 있는 변수라고 생각하기 보다는 nil까지 담을 수 있는 상자라고 생각하면 이해가 쉬울것이다.
변수되에 ? 를 붙여서 선언할 수 있다.
var job: String = "개발자"
print(job)
실행결과는 "개발자"라는 문자열이다.
var job: String
print(job)
그런데 타입만 정해주고, 값은 정해주지 않은 상태로 출력을 하게되면 오류가 생길것이다.
var job: String?
print(job)
이 때, ? 를 붙여서 옵셔널로 만들고 출력을 하게 되면 nil이 출력된다. 옵셔널 변수는 값을 가지지 않더라도 오류가 발생하지 않고, nil 값을 가지게 되는 것이다.
var job: String? = "개발자"
print(job)
옵셔널 변수에 값을 넣은 후에 출력을 하게 되면 Optional("개발자") 라고 출력이 된다.
옵셔널로 만든 변수에 값을 할당하더라도, 그 결과는 Optional()을 포함한 상태였다. 이제 상자, 즉 Optional()을 꺼내는 과정이다.
가장 직접적이고 직관적인 방법이다. 변수명 뒤에 ! 를 붙이는 것이다.
var job: String? = "개발자"
print(job!)
이렇게 되면 "개발자"만 출력이 된다. 그렇지만 변수에 값이 할당이 되었는지 확인을 하지 않고, 강제로 언래핑하기 때문에 굉장히 위험한 방식이다.
var job: String?
print(job!)
이렇게 실행을 시키면 Execution was interrupted, reason: EXC_BREAKPOINT
오류가 발생한다.
Expression implicitly coerced from ‘String?’ to ‘Any’
이런 경고도 볼 수 있는데, nil coalescing을 사용하면 된다.
Nil coalescing은 변수의 값이 nil일 경우에 값을 지정해주는 것을 의미한다. 사용은 변수명 ?? 값 이렇게 하면 되고, 변수의 값이 nil일 경우 값을 할당한다는 의미이다.
var job: String?
print(job ?? "없음")
출력결과는 "없음"이다.
옵셔널 바인딩은 좀 더 안전한 방식으로 nil인지 먼저 확인하고 추출하는 방식이다.
if let
을 이용하는 경우
var job: String? = "개발자"
func printJob(job: String?) {
if let jobName = job {
print(jobName)
} else {
print("없음")
}
}
printJob(job: job) // "개발자"
job이라는 변수가 값을 가지고 있다면 jobName이라는 새로운 변수에 값을 할당한 후, true를 반환하여 해당 코드가 실행된다. 값이 없다면 false를 반환하여 else 코드가 실행된다. 핵심 코드인 if let jobName = job
의 의미는 만약 옵셔널이 아닌 jobName 변수에 옵셔널인 job 값을 할당 할 수 있으면 이라고 생각하면 쉽다.
guard let
을 이용하는 경우
guard let 은 함수에서만 쓰일 수 있기 때문에 함수로 만들어야 한다.
var job: String? = "개발자"
func printJob(job: String?) {
guard let jobName = job else {
print("없음")
return
}
print(jobName)
}
printJob(job: job) // "개발자"
job이 값을 가지고 있는지 확인 후, 값이 없다면 else 에 해당하는 코드를 실행하고 그 값을 반환하여 guard 에서 나온다. job이 값을 가지고 있다면 else 를 지나쳐 print(jobName) 을 실행한다.
두가지 방식의 차이는 다음과 같다.
if let
: 값의 유무를 확인 후, 있을 때 없을 때 각각의 코드를 실행guard let
: 값의 유무를 확인 후, 값이 없으면 함수를 실행하지 못하도록 방어if let
> guard let
옵셔널을 반복사용하여 옵셔널이 체인처럼 꼬리에 꼬리를 물고 있는 모양이다. 체인처럼 서로가 이어져있기 때문에 중간에 하나라도 값이 존재하지 않으면 (nil이면) 바로 nil을 반환한다.
옵셔널 체이닝은 다음과 같은 문법으로 표현된다.
optionalValue?.property
optionalValue?.method()
여기서 optionalValue는 옵셔널 값이고, property는 속성 이름, method()는 메서드 호출이다. 옵셔널 체이닝 연산자 ?는 옵셔널 값이 nil이 아닌 경우에만 속성에 접근하거나 메서드를 호출하라는 의미이다. 만약 옵셔널 값이 nil인 경우, 체이닝된 표현식은 자동으로 nil을 반환한다.
class Person {
var name: String
var address: Address?
init(name: String) {
self.name = name
}
func printAddress() {
if let city = address?.city {
print("City: \(city)")
} else {
print("No address available")
}
}
}
class Address {
var city: String
init(city: String) {
self.city = city
}
}
let person = Person(name: "Bob")
person.printAddress() // 출력: "No address available"
person.address = Address(city: "New York")
person.printAddress() // 출력: "City: New York"
printAddress 메서드는 옵셔널 체이닝을 사용하여 address의 city 속성에 접근하고 출력한다. person 객체가 주소 정보를 가지고 있을 때에만 "City" 값을 출력하고, 그렇지 않은 경우에는 "No address available"을 출력한다.
let numbers: [Int]? = [1, 2, 3, 4, 5]
let firstNumber = numbers?[0] // numbers가 nil이 아니면 첫 번째 요소에 접근
let fifthNumber = numbers?[4] // numbers가 nil이 아니면 다섯 번째 요소에 접근
print(firstNumber) // 출력: Optional(1)
print(fifthNumber) // 출력: Optional(5)
배열 numbers가 옵셔널이며 옵셔널 체이닝을 사용하여 배열의 요소에 접근하는 예시이다. 배열이 nil이 아닌 경우에만 요소에 접근하고 값을 반환한다.