Swift

[ Swift ] Swift 기초 문법4 - 옵셔널

kybeen 2023. 1. 28. 15:56
728x90
반응형

[ Optional ]

옵셔널이란 값이 있을 수도 있고, 값이 없을 수도 있다는 것을 뜻한다.

let optionalValue: Optional<Int> = nil
let optionalValue: Int? = nil

 

Optional이 필요한 이유?

  • nil의 가능성을 명시적으로 표현해줄 수 있기 때문이다. (nil의 가능성을 문서화 하지 않아도 코드만으로 충분히 표현 가능)
  • 전달받은 값이 옵셔널이 아니라면 nil 체크를 하지 않더라도 안심하고 사용할 수 있다.

 

아래 예제와 같이 Int?(옵셔널) 타입이 명시된 함수에는 nil을 전달할 수 있지만 그렇지 않은 함수에는 nil을 전달할 수 없다.

 

 

 

Optional의 표현

  • ! (Implicitly Unwrapped Optional, 암시적 추출 옵셔널)
// [ 암시적 추출 옵셔녈 ]
var optionalValue: Int! = 100

// Optional 타입 자체는 열거형이기 때문에 switch case문으로 구분해줄 수 있다.
switch optionalValue {
case .none: // 값이 없으면
    print("This Optional variable is nil")
case .some(let value): // 값이 있으면
    print("Value is \(value)")
}
/*
Value is 100
*/

// 기존 변수처럼 사용 가능
optionalValue = optionalValue + 1
// nil 할당 가능
optionalValue = nil
// But 잘못된 접근으로 인한 런타임 오류 발생 가능
optionalValue = optionalValue + 1

!로 표현된 옵셔널은 기존 변수처럼 사용하며 nil도 할당 가능하다.

하지만 nil이 할당된 채로 기존 변수처럼 사용하는 등의 잘못된 접근으로 인한 런타임 오류가 발생할 수 있다.

 

 

var optionalValue: Int? = 100

// Optional 타입 자체는 열거형이기 때문에 switch case문으로 구분해줄 수 있다.
switch optionalValue {
case .none: // 값이 없으면
    print("This Optional variable is nil")
case .some(let value): // 값이 있으면
    print("Value is \(value)")
}

// nil 할당 가능
optionalValue = nil
// 하지만 기존 변수처럼 사용은 불가 ==> 옵셔널과 일반 값은 다른 타입이므로 연산 불가
optionalValue = optionalValue + 1

?로 표현된 옵셔널은 nil 할당은 가능하지만 기존 변수처럼은 사용할 수 없다.

옵셔널 타입과 일반 타입을 확실하게 구분하여 잘못된 접근을 애초에 예방해준다.

 

 

그렇기 때문에 옵셔널 타입의 값은 다른 방식으로 추출하고 접근해주어야 한다.

 

 

 

[ 옵셔널 값 추출 - Optional Unwrapping ]

옵셔널 값을 추출하는 방법은 2가지가 있다.

 

 

1. Optional Binding - 옵셔널 바인딩

옵셔널의 값을 꺼내오는 방법 중 하나로

nil체크 + 안전한 값 추출 이 가능하다.

옵셔널 값을 꺼내오기 전에 한 번 물어보는 것처럼 생각하면 좋다. (값이 있으면 꺼내오고, 아니면 안꺼냄)

 

아래 코드에서는 String 타입의 매개변수를 받는 함수에 Optional 타입의 변수를 넣어준다.

String과 Optional은 다른 타입이기 때문에 오류가 발생한다.

func printName(_ name: String){
    print(name)
}

var myName: String? = nil
printName(myName) // 전달되는 값의 타입이 다르기 때문에 컴파일 오류 발생

 

  • if - let 방식

if-let 방식을 통해서 옵셔널 바인딩이 가능하다.

func printName(_ name: String){
    print(name)
}

var myName: String! = nil

if let name: String = myName {
    printName(name)
} else {
    print("myName == nil")
}
// name 상수는 if-let 구문 내에서만 사용 가능

name 상수를 if-let 구문 밖에서 사용 시에는 컴파일 오류가 발생한다.

 

 

한번에 여러 옵셔널 변수들도 바인딩 가능하다.

var myName: String? = "kybeen"
var yourName: String? = nil

if let name = myName, let friend = yourName {
    print("\(name) and \(friend)") // myName과 yourName 모두 값이 들어 있어야 실행
}
// yourName이 nil이기 때문에 실행되지 X

yourName = "YOU"
if let name = myName, let friend = yourName {
    print("\(name) and \(friend)")
}
// kybeen and YOU

 

 

 

2. Force Unwrapping - 강제 추출

옵셔널의 값을 강제로 추출하는 방식.

옵셔널 값을 꺼내오기 전에 물어보지 않고, 강제로 꺼내는 방식이라고 생각하면 된다.

 

func printName(_ name: String) {
    print(name)
}

var myName: String? = "kybeen"
printName(myName!) // !를 붙여주면 옵셔널 값이 강제로 추출돼서 전달

myName = nil
print(myName!)
// nil값이 들어있으면 강제추출 시 값이 없으므로 런타임 오류 발생

// 암시적 추출 옵셔널 방식은 선언할 때부터 위와 같은 상황을 염두에 두는 것임
var yourName: String! = nil
printName(yourName) // ! 안붙여줘도 된다. yourName은 nil값이기 때문에 런타임 오류 발생

 

 

 

 

 

 

 

오류 가능성이 있는 강제추출 방식 보다는 안전하고 확실한 방법인 옵셔널 바인딩을 사용하도록 하자!!!

  • ! 말고 ?로 타입 명시
  • if - let 구문 사용

 

 

 

 

 

 

728x90
반응형