Swift

[Swift] Optional이란?

Hatchling.dev 2023. 6. 5. 22:36

(편의상 편한 말투로 작성하는 점 이해 부탁드립니다.😅)

(부정확한 정보가 있을 수 있습니다. 지적 환영🤩)

 

Optional

오늘은 Optional에 대해서 알아보려 합니다!
Optional이란 무엇인가... 직역하면 선택적인 이라는 뜻을 가지고 있네요.

Swift에서 Optional은 값이 있을 수도, 없을 수도 있는 상황에서 쓰이게 됩니다.

 

코드로 보며 이해해보겠습니다.

var number1: Int = nil	// 'nil' cannot be assigned to type 'String'
var number2: Int? = nil

(nil은 다른 언어의 null과 같으며 비어있다는 뜻을 가지고 있습니다!)

위와 같이 일반적인 문자열 타입의 변수에는 nil을 할당하지 못하지만, 타입 뒤에 ? 나 !를 붙이면 nil을 할당할 수 있습니다. ?와 !의 차이는 아래에서 더 설명하도록 하겠습니당 그렇다면 왜 nil이 필요할까에 대한 의문이 들 수 있겠죠?? 우리는 개발하다보면 값이 비어있는 경우를 생각보다 자주 겪을 수 있습니다.

위와 같이 Int 타입의 변수에 초기화되지 않은 변수를 사용하려고 한다면 어떻게 해야할까요? 간단하게 0을 넣는 방법도 있겠지만 0와 값이 없는 경우는 엄연히 다릅니다! 이같이 값이 없는 경우에 사용하는 것이 Optional이라고 생각하면 됩니다! (쉽나요..?)

 

Optional의 정의와 간단한 선언 방법을 알아봤으니 이제 사용방법을 알아봐야겠죠??

먼저 옵셔널이 아닌 변수를 옵셔널 변수에 할당하는 것은 불가능합니다. 왜 그럴까요?? 이 둘은 엄연히 다른 타입이기 때문입니다. 예를 들면 메모리 주소값을 저장하는 변수에 해당 변수의 값을 할당하지 못하는 것과 같은 개념이라고 생각하면 쉬울 것 같네요!

그렇다면 옵셔널 타입은 어떻게 사용하는지 알아보도록 하겠습니다~

 

var number1: Int = 0
var number2: Int? = 0

print(number1)	// 0
print(number2)	// Optional(0)

옵셔널이 아닌 변수와 옵셔널 변수 모두 0으로 초기화하고 출력해보겠습니다. 출력 값이 다르죠? 옵셔널 변수는 Optional()에 감싸져 있는 것을 확인할 수 있습니다. 우리는 옵셔널 변수의 0을 사용하기 위해서는 해당 옵셔널을 해제해줘야 합니다. 그럼 이제 옵셔널을 해제하는 방법을 알아야겠죠? (wrapping = 값을 Optional로 감싸는 것, unwrapping = Optional을 해제하는 것)

 

Optional Unwrapping

옵셔널을 해제하는 방법은 여러가지가 있습니다.

  1. 명시적 해제
    • 강제 해제
    • Optional Binding (옵셔널 바인딩)
  2. 묵시적 해제
    • 컴파일러에 의한 자동 해제
    • 옵셔널의 묵시적 해제

옵셔널 체이닝 방법도 있지만 아래에서 더 설명하기로 하고 위 방법들을 어떻게 사용해야 하는지 코드를 통해 살펴보겠습니다!

 

강제 해제

옵셔널 변수를 사용할 때 !를 붙여 옵셔널을 강제로 해제하는 방법입니다. 가장 간단한 방법이지만 해당 변수에 nil이 할당되어 있다면 런타임 에러가 발생하기 때문에 확실히 값이 있을 때만 사용하는 것이 좋습니다. (저는 확실히 값이 있더라도 되도록 지양하긴 합니다 ㅎ..)

var number2: Int? = 0

print(number2)	// Optional(0)
print(number2!)	 // 0

 

Optional Binding

if나 guard 구문 내에 조건문 대신 옵셔널 값을 변수나 상수에 할당하여 사용하는 방법입니다. 

 

if문은 변수나 상수를 해당 구문 내에서만 사용이 가능하며 옵셔널 변수에 값이 있을 때(nil이 아닐 때) 실행됩니다.

if let num = number2 {
    print("number2의 값은 \(num)입니다.")
} else {
    print("number2의 값은 비어있습니다.")
}

guard문은 옵셔널에 값이 없을 때(nil일 때) 실행되는 구문으로 guard문으로 생성된 변수나 상수를 구문 밖에서도 사용할 수 있습니다. guard문은 옵셔널이 nil일 때 해당 작업을 더이상 진행하지 않고 멈추게 됩니다.

guard let num = number2 else {
    print("number2의 값은 비어있습니다.")
    return
}
print(print("number2의 값은 \(num)입니다."))

 

컴파일러에 의한 자동 해제

옵셔널 타입을 연산자로 비교할 때 컴파일러가 자체적으로 옵셔널을 해제하고 비교하게 됩니다. 하지만 비교할 때만 옵셔널을 해제하고 비교하는 것이기 때문에 옵셔널이 해제된 값을 사용할 수는 없습니다.

if number2 == 10 {
    print("number2는 10입니다.")
} else {
    print("number2는 10이 아닙니다.")
}

 

옵셔널의 묵시적 해제

위에서 옵셔널 선언하는 방법을 설명할 때 ? 나 !를 붙여 선언할 수 있다고 했는데 !를 붙여 선언하면 해당 옵셔널 타입을 사용할 때 묵시적으로 옵셔널을 해제해줍니다.

var number2: Int? = 10
number2 += 1	// Value of optional type 'Int?' must be unwrapped to a value of type 'Int'

var number3: Int! = 10
number3 += 1

 

Optional Chaining (옵셔널 체이닝)

옵셔널 타입을 해제하지 않고 메서드나 프로퍼티를 실행해도 에러를 발생시키지 않습니다.

struct Student {
    var neme: String
}

var student1: Student?
var student2: Student? = Student(neme: "woo")

print(student1?.neme)	// nil
print(student2?.name)	// Optional("woo")

옵셔널 체이닝으로 참조된 값은 옵셔널로 반환된다.

var name = student?.neme
print(name)	// Optional("woo")

참고로 옵셔널 체이닝할 때 ? 대신 !를 쓸 수 있지만 강제 언래핑과 같이 nil일 땐 런타임 에러가 발생하기 때문에 조심해야 합니다!

 

오늘은 옵셔널에 대해 알아보았습니다!

평소에 자주 쓰기 때문에 잘 알아두는 것이 좋겠네요!!

다음에는 구조체에 대해 알아보도록 하겠습니다~~