개요

💡 옵셔널 체이닝 등 일부 빠진 개념은 좀 더 공부하고서 채울 예정
💡 스위프트에서는 `NULL` 을 `nil` 로 표기한다.

스위프트를 처음 접하면서 가장 먼저 만나는 생소한 문법은 Optional 이 아닐까 생각한다. 코틀린에는 Nullable 이라는 개념이 있지만 내가 주로 사용했던 자바나 파이썬에는 없는 문법이었다. (Java8 부터 도입되었다.)

일단, Optional에 대해서 알아보자.

The Swift Programming Language (Swift 5.3) 페이지에서 Optional을 이렇게 설명한다.

You use optionals in situations where a value may be absent. An optional represents two possibilities: Either there is a value, and you can unwrap the optional to access that value, or there isn’t a value at all.

값이 없을 수 있는 상황에서 옵셔널을 사용합니다. 옵셔널은 두가지 가능성을 나타낸다: 값이 있고 옵셔널을 풀어서 해당 값에 액세스할 수 있거나 값이 전혀 없는 경우.

옵셔널 변수(또는 상수)를 만드는 법

Int형 변수를 만든다고 해보자. 우리는 모종의 이유로 변수를 생성하고 나중에 값을 초기화해주고 싶다. 그리고 이러한 상황을 파이썬으로 작성한다면 다음과 같다.

num = None
num = 7
print(num) # 7

나중에 넣은 7이 잘 출력되는 걸 확인할 수 있다. 똑같이 스위프트로 작성하면 어떨까?

var num: Int = nil // 🛑 'nil' cannot initialize specified type 'Int'
num = 7
print(num)

스위프트는 nil에 대해서 엄격한 편이다. 그래서 우리가 평소에 다른 언어에서 사용하던 것처럼 변수 생성 시 nil을 넣고 나중에 초기화 하려고 하면 Xcode에서는 'nil' cannot initialize specified type 'Int' 이라는 문구와 함께 빨간줄이 뜬다.

위 상황처럼 일반 변수에 nil을 할당하려고 하면, 컴파일 오류가 우리를 반긴다. 스위프트에서는 옵셔널 변수(상수)가 아니면 nil을 할당할 수 없다.

옵셔널 변수(상수)를 생성하고 nil을 할당해보자. 타입 뒤에 ?를 붙이면 된다.

var num: Int? = nil
num = 7
print(num) // Optional(7)

num변수에 7을 넣고 출력을 해보니 출력이 되긴 하는데, 뭔가 이상하다. 값이 Optional()로 감싸져서 출력된다. 이제, 옵셔널 값을 과일 깎듯 알맹이만 뽑아내는 방법을 알아보자.

강제 추출(Forced Unwrapping)

옵셔널 값을 강제로 추출하려면 옵셔널 값 뒤에 !를 붙인다. 이 방법은 확실하지만 위험하다. 벗겨내려는 옵셔널이 값이 있다면 문제가 없겠지만, 값이 없는 상황이라면 오류가 우리를 반긴다.

var num: Int? = 7
if num != nil {
	print("num is \(num!)") // num is 7
}

가급적이면 사용하지 않고, 혹시라도 쓰게 된다면 반드시 값이 있음을 보장하는 상황에서만 쓰자.

옵셔널 바인딩(Optional Binding) - if

옵셔널 바인딩은 옵셔널 변수(상수)에 값이 있는지 확인하고, 해당 값을 임시 변수(상수)에 할당해서 사용하는 기법이다. if문이나 while문과 함께 사용된다.

func output(_ myName: String?) {
	if let name = myName {
		print("my name is \(name)")
	} else {
		print("unknown name")
	}
}

var myName: String? = "지원"
output(myName) // my name is 지원

if문에서 생성한 임시 상수 nameif문 내부에서만 사용할 수 있다.

위와 같은 상황을 guard문을 이용해서 처리할 수도 있다.

옵셔널 바인딩(Optional Binding) - guard

guard문에 있는 조건이 false인 경우에는 guard문 내부로 진입하고, true인 경우에는 바깥 코드를 진행한다.

if문으로 생성한 임시 상수는 if문 내부에서만 사용할 수 있지만, guard문으로 생성한 상수는 guard구문이 끝나고 난 뒤부터 함수 내부의 지역 상수처럼 사용할 수 있다.

func output(_ name: String?) {
	guard let name = myName else {
		print("unknown name")
		return
	}
	print("my name is \(name)")
}

var myName: String? = nil
output(myName) // unknown name

guard문 내부의 끝에는 return, break, continue, throw가 포함되어야 한다.

Nil-Coalescing Operator - ??

??연산자는 값이 포함되어 있는 경우에는 옵셔널을 벗긴 값을 반환하고, 없는 경우에는 default 값을 설정해줄 수 있다.

var myNickname: Int? = nil
let nickname = myNickname ?? "먹구름"
print(nickname) // "먹구름"

!!

Double exclamation !! mark in Swift? 이라는 스택오버플로우 글을 보면, Optional(Optional(value))와 같이 이중으로 포장 되어있는 상황에서 사용하는 거 같지만, 더 자세한 자료는 찾지 못해서 일단 이 부분은 좀 더 찾아보는 걸로….

참고 자료