iOS 개발 기록

Swift - 옵셔널 (Optional) 본문

Swift

Swift - 옵셔널 (Optional)

택꽁이 2022. 7. 19. 18:40
728x90

옵셔널이란 ?

Swift 언어의 큰 특징으로 옵셔널을 뽑을 수 있을 것이다. 

옵셔널이란 값이 없을수도 있다는 가능성을 열어두는 것으로, '?'라는 키워드로 표현하며 값이 없는 상태는 nil값을 가진다. 

var a: Int = nil 		// error
var b: Int? = nil

 

 

또한 옵셔널은 옵셔널이 아닌 데이터 타입과 다르게 취급된다. 

예를 들면 위의 예제 코드의 a와 b가 같은 값을 가지고 있다 하더라도 그 둘은 다른 데이터 타입을 가지게 된다. 

a의 경우 그냥 1이지만 b의 경우 Optional(1)로 출력한다는 것을 확인할 수 있다.

b의 경우 이 변수가 언제 nil을 가지게 될 지 모르기 때문이다.

재밌게도 값은 둘다 1이기 때문에 둘을 비교했을때 같다고 출력한다. 

var a: Int = 1 
var b: Int? = 1 

print(a)
print(b)
print(a == b)

위 코드의 출력값

 

 

그렇다면 옵셔널 값을 일반 값으로 얻기 위해서는 어떻게 해야할까?

이를 옵셔널 언래핑이라고 하는데, 총 세가지를 설명한다. 

 

 

 

 

1. 강제 언래핑 

뒤에 '!'라는 키워드를 붙이는 것을 강제 언래핑이라고 한다.

말 그대로 옵셔널의 값과 상관 없이 강제로 언래핑 한다.

다음 코드의 경우 Optional이라는 키워드가 사라진 것을 확인할 수 있다. 

var a: Int? = 1 
print(a!)

강제 언래핑

 

하지만 강제 언래핑의 경우 함부로 쓰면 안되는데, 강제 언래핑은 옵셔널에 값이 존재하지 않을 경우 치명적인 에러가 발생한다. 

때문에 값이 반드시 있을 경우에만 사용하고, 왠만하면 옵셔널 바인딩과 옵셔널 체인딩으로 언래핑 하는것이 좋다.

nil을 강제언래핑 하려 했기 때문에 에러가 든다.

 

 

 

2. 옵셔널 바인딩

옵셔널 바인딩은 옵셔널을 안전하게 언래핑하는 방법이다.

보통은 if let 혹은 guard let 구문을 사용하게 된다.

이는 옵셔널에 값이 있다면 그 값을 임시로 변수나 상수에 할당하고, 옵셔널 값이 nil일 경우 로직을 넘기는 형태로 사용한다. 

 

- if let 

// if let 
var a: Int? = nil
var b: Int? = 1 

if let new = a { 
    print(new)        // a가 nil이기 때문에 
} else { 
    print(a)        // else 구문이 실행된다.
}

if let new = b { 
    print(new)        // b는값이 있기 때문에 if 구문에서 실행된다.
}

코드의 출력 결과

 

해당 코드에서 a의 경우 값이 없기 때문에 if let 의 구문을 넘어가고 else 구문이 실행되어 nil을 출력했다. 

b의 경우 값이 있기 때문에 해당 값을 상수 new에 할당해 if 구문에서 언래핑 된 채로 사용한 것을 볼 수 있다.

 

 

 

- guard let

var a: Int? = nil
var b: Int? = 1 

func binding()  { 
    guard let newB = b else { return print("b는 값이 없어요!") }
    print(newB)
    
    guard let newA = a else { return print("a 는 값이 없어요!")} 
}

binding()

출력 결과

 guard let 구문은 else 부분만 작성이 가능하다. 

guard let 구문은 옵셔널 값이 nil이라면 else 부분의 로직을 실행한다.

nil이 아니라면 옵셔널의 값을 guard let으로 선언한 상수에 할당한다.

 

 

 

- if let 과 guard let의 차이

1. if let의 경우 언래핑한 값을 if 구문 안에서만 사용할 수 있지만 guard let은 구문 실행 후 해당 함수 내에 지역상수처럼 계속 사용할 수 있다. 

2. guard let의 else 구문에는 return, break, continue 등 제어문 전환 명령어가 들어간다. 때문에 함수나 메서드 등 상위 코드 블록이 있어야만 사용할 수 있다. 

 

 

이처럼 옵셔널 바인딩을 사용하면 강제로 언래핑하는 것 보다 안전하게 언래핑할 수 있다.

 

 

 

 

 

3. 옵셔널 체이닝 

명확하게 옵셔널 체이닝은 위의 두개와 다르게 옵셔널을 벗겨내는 방법은 아니다. 

옵셔널 체이닝은 옵셔널에 접근할 때에 옵셔널을 다루는 방식으로, 이름 그대로 연쇄적으로 옵셔널에 접근하는 방법이다.

옵셔널에 접근하는 과정 속에서 nil이 존재한다면 nil을 반환하고, 그렇지 않다면 언래핑된 값을 반환한다. 

옵셔널 체인이은 접근하려는 프로퍼티에 '?'  키워드를 붙여 사용한다.

 

// 옵셔널 체이닝
struct Hop { 
    var name: String
    var region: String
}

struct Beer { 
    var name: String
    var contry: String
    var hop: Hop
    
    init(name: String, contry: String , hop: String, region: String) {
        self.name = name
        self.contry = contry
        self.hop = Hop(name: hop, region: region)
    }
}

var life: Beer? = Beer(name: "life", contry: "Korea", hop: "citra", region: "america")
print(life?.hop.name)		// life 가 옵셔널 값이므로 뒤에 '?' 키워드를 붙여 옵셔널 체이닝으로 접근

life = nil 
print(life?.hop.name)

출력 결과

 

 

 

 

다음과 같이 옵셔널 체이닝을 사용하면 해당 변수가 nil인지 아닌지를 확인하고, 아닌 경우 Optional 값을 반환한다. 

그러나 name까지 접근하는 중에 nil값이 있다면 해당 값은 nil을 반환한다. 

보통은 옵셔널 체이닝과 옵셔널 바인딩을 사용해서 해당 값을 언래핑한다. 

 

 

 

// 옵셔널 체이닝 + 옵셔널 바인딩
var life: Beer? = Beer(name: "life", contry: "Korea", hop: "citra", region: "america")

if let hopName = life?.hop.name { 
    print(hopName)
}

언래핑한 출력 결과