Swift의 Optional Unwrapping 방법들

728x90

정의

Optional은 nil을 사용할 수 있는 타입과 없는 타입을 구분하기 위함이다.
let name: String?과 같은 형태로 ?를 붙여서 사용한다
Optional Type이 아닌 Type에는 당연히 nil이 주입될 수 없다.

Optional 은 Swift 언어의 안정성에 해당한다.
찾는값이 없다고 nil을 발생시켜 Runtime Error가 생기는걸 방지하고 개발자가 이를 대처할 수 있게 해준다.

Optional Unwrapping

Optional Type인 변수는 그냥 사용하려하면 뭔가 잘 안될것이다.
그 이유는 Int를 예로들어, Optional Type인 변수는 Optional<Int> 형식으로 되어있기 때문이다.
그대로 출력해 보면 Optional(4)와 같은 형식으로 출력된다.

이를 제대로 사용하기 위해서는 Unwrapping 과정이 필요하다.
Unwrapping 방법을 알아보도록 하자!

간편한 방식 (잘 사용하지 않는 방식)

코드만 첨부할건데, 지금 설명할 방식은 잘 사용하지 않고 이렇게도 된다라고만 알면 된다

var name: String? = "Name"
if name != nil { print(name) }
else { print("Name is nil) }

강제 언래핑 (Forced Unwrapping)

Optional Type의 값이 nil이든 뭐든 무조건 Optional 제거!! 와 같은 방식으로
간단하게 !를 사용하면 된다.
코드로 보면..

let a: Int? = 10 // Optional<Int> 선언
print(a!) // 결과 "10"

정말 편한거 같지만 만약, nil 값을 강제 언래핑한다면 컴파일 에러가 일어난다.
만약 컴파일이 확인 못한다면... 런타임 에러가 발생하게된다.

그렇다면 안전하게 Unwrapping 하는 방식은?

옵셔널 바인딩 (Optional Binding)

Swift를 사용해 개발할 때 가장 많이 사용할 방식이며 안전하게 unwrapping 할 수 있다.
3가지의 syntax를 활용하면 된다.

if let 방식

let num: Int? = 10
if let temp = num { print(temp) } // 10
else { print("num is nil") }

if let구문을 이용해 해당 변수를 임시 구문 블럭 변수에 주입시키고
nil이 아니라면 unwrapping 된 type을 사용할 수 있게 된다.
nil인 경우에는 else 구문을 실행한다.

여러개의 Optional Type에 대해서도 옵셔널 바인딩을 쓸 수 있다!

let num: Int? = 10
let num2: Int? = nil
if let temp = num, let temp2 = num2, temp >= 10
    { print(temp) } // 10
else 
    { print("num is nil") }

동시에 조건도 같이 걸 수 있으니 언래핑 후 값이 조건에 맞을 때 사용하는 방식도 가능하다.

guard let 방식

let num: Int? = 10
guard let temp = num else { return }

[[Guard문]] 에서 unwrapping이 실패하면 else 구문의 동작을 실행하게된다.
guard의 특성상 함수(메서드)에서만 쓰이고 else 구문에는 반드시 해당 함수를 탈출해야한다.??

만약 정상적으로 unwrapping 됐다면 함수 내에서 unwrapping에 사용된 변수를 그대로 사용할 수 있다.
그리고 if-let 구문과 동일하게 여러개를 unwrapping 하면서 동시에 조건도 걸 수 있다.

==Swift 5.7 부터 Optional Binding에 단축구문을 도입했다.==
if let num { ... }이런 방식으로 사용할 수 있다 (guard let 도 마찬가지)
하지만 guard let의 경우 특정 인스턴스 안의 멤버 변수를 단축구문으로 사용할수는 없다.

while let 방식

사용하지 않는편이다.
그래도 그냥 보기만 하자

var numbers: [Int?] = [1, 2, nil, 4, nil, 6]

while let number = numbers.popLast() {}

암시적 언래핑 옵셔널 (Implicitly Unwrapped Optional)

옵셔널 묵시적 추출로도 불리며
별도의 추출 과정을 거치지 않아도 자동으로 옵셔널이 해제되는 것이다.
let name: String! 처럼 ?말고 !를 붙여주면 된다.

이렇게 선언해도 여전히 Optional type이지만, 자동으로 Unwrapping을 해준다.
하지만 조건이 있다.

let num: Int! = 4
let temp: Int = num

이 경우 자동으로 언래핑을 해준다.
그 외에 num을 출력하거나 그대로 사용한다면 여전히 Optional 타입으로 표시된다...

이걸 정말 사용할까?

개발하면서 별로 사용할 거 같진 않지만 사실 IBOutlet을 쓸 때 사용된다.

스토리 보드에서 코드를 연결했을 때 @IBOutlet weak var tableView: UITableView!와 같이 암시적 언래핑 옵셔널을 사용하는걸 볼 수 있다.

그 외에는 쓸 생각말자 옵셔널 바인딩 하는게 백배 낫다.

왜 IBOutlet은 암시적 언래핑 옵셔널을 사용할까?

IBOutlet은 StoryBoard의 View 객체와 Interface Builder를 연결한다.
강제 언래핑은 nil이 아니라는 것이 확실할 때만 사용한다.
Swift는 불필요한 객체 등의 선언을 지양한다.

종합하면,
IBOutlet으로 선언한 View가 nil일 경우가 없다고 생각하는 것이고,
만약 nil이라면 UI 작성 중 잘못된것이 분명하므로 크래시를 내서라도 알리는 것으로 보임.

옵셔널 체이닝 Optional Chaining

Chaining이 연쇄니까 말 그대로 Optional을 연쇄 사용 하는것이다.
내부 프로퍼티나 메서드를 연속해서 접근할 때 옵셔널이 하나 이상 있다면 옵셔널 체이닝이라 부른다.

코드로 보고 이해하는게 빠르다

//먼저 구조
struct A { var b: B? = B() }
struct B { var num: Int = 0 }

let a = A()
a.b?.num

옵셔널 값에 접근할 때 ?.을 써서 접근해가는 방식을 옵셔널 체이닝이라 한다.

특징

  1. 옵셔널 체이닝의 결과값은 항상 Optional이다.
  2. 옵셔널 체이닝 중 마지막 표현식이 옵셔널이라면 ?를 생략한다.
  3. 옵셔널 체이닝 중 하나라도 nil이라면, 평가하지 않고 바로 nil을 반환한다.

함수의 옵셔널 체이닝

함수의 반환값이 Optional인 경우
    function()?.num
함수 자체가 Optional인 경우
    function?().num
    let function = obj?.function 처럼 optional function 가능
둘 다
    function?()?.num

딕셔너리의 옵셔널 체이닝

기본적으로 Key가 없을수도 있기때문에 Optional Type을 리턴한다.
    dict["key"]?.value
딕셔너리 자체도 Optional인 경우 let dict: [String: Int]? = ...
    dict?["key"]?.value

?? 연산자 (Nil-Coalescing Operation)

Optional Type에 저장되어 있는 값을 굳이 꺼내서 쓰지 않고 간편히 쓸 수 있는 방법 중 하나다.

사용 방법은 간단하다. print("Hello I'm \(name ?? "KIM")
name 변수가 Optional일때, nil이라면 오른쪽의 값을 보내주고 아니면 언래핑 된 값을 보내준다.

728x90