Weekoding

[Swift] 옵셔널(Optional) 본문

공부 노트(Swift)

[Swift] 옵셔널(Optional)

Weekoding 2022. 4. 17. 03:57

옵셔널에 대해 좀 더 명확한 개념을 갖고자 이 글을 쓰기로 계획했다.

 

< 목차 >

   1. 옵셔널이란?

   2. 옵셔널 사용

   3. 옵셔널 추출(Optional Unwrapping)
       - 강제 추출(Forced Unwrapping)

       - 옵셔널 바인딩(Optional Binding)

       - 암시적 추출 옵셔널(Implicitly Unwrapped Optional)

   4. 심화 - 옵셔널 뜯어보기

 

 

1. 옵셔널이란?

옵셔널(Optional): '선택적인' 이라는 뜻을 가진 단어. Swift에서 값이 있을수도, 없을수도 있음을 나타내는 표현이다. 

즉, 변수/상수에 값이 nil일 수도 있다(=값이 있는 것을 보장할 수 없다) 라는 의미이다.

Swift의 특징 중 하나인 '안전성'을 문법으로 담보하는 기능이다. 

옵셔널 변수 또는 상수가 아니면 nil을 할당할 수가 없다.(할당 시에는 런타임 오류가 발생한다.)

 

 

2. 옵셔널 사용

옵셔널 상수나 변수는 데이터 타입 뒤에 물음표(?)를 붙여 표현할 수 있다.

 

옵셔널을 사용하는 이유는, 예를 들자면

ex1) 내가 만든 함수에 전달되는 전달인자의 값이 잘못된 경우, 처리하지 못했음을 런타임 에러가 아닌 간단히 nil로 반환하여 표현할 때

ex2) 매개변수를 굳이 넘기지 않아도 된다는 뜻을 표현하고 싶을 때

정도가 있을 것이다.

물음표 하나만으로 값이 없어도 되는, 혹은 없을 수도 있다는 커뮤니케이션이 가능한 것이다. 

//데이터타입(String)뒤에 물음표를 붙여 옵셔널 변수를 선언한다.
var dog: String? = "pitbull"
print(dogName)
//Optional("pitbull")

//특징으로는 그냥 "pitbull"이 아닌, Optinal()로 감싸져서 값이 출력되는 것
//이는 추후 서술되는 옵셔널 추출을 통해 벗겨낼 수 있다.
//추가) nil을 할당하면, print함수 사용시 'nil'이 출력된다

 

 

3. 옵셔널 추출(Optional Unwrapping)

옵셔널이 아닌 변수에는 옵셔널 값이 들어갈 수 없다.

따라서 옵셔널이 아닌 값으로 추출해서(Optional() 껍데기를 벗겨내는 작업) 할당해주어야 한다.

 

   - 강제 추출(Forced Unwrapping)

        가장 간단한 방법이면서, 가장 위험한 방법이다.

        옵셔널 값의 뒤에 느낌표(!)를 붙여주면 값을 강제로 추출하여 반환받을 수 있다.

       ❗️: 강제 추출시 옵셔널에 값이 없다면(nil이면), 런타임 오류가 발생한다.

             따라서 런타임 오류가 일어날 가능성이 가장 높고, 런타임 오류의 가능성을 항상 내포하기 때문에 강제 추출은 지양해야 한다.

var dog: String? = "pitbull"
var puppy: String = "Husky"

dog = nil
puppy = dog! //runtime Error

     

   - 옵셔널 바인딩(Optional Binding)

       옵셔널에 값이 있는지 확인할 때 사용

       옵셔널에서 추출한 값을 일정 블록 안에서 사용할 수 있는 상수나 변수로 할당해서 사용할 수 있게 해준다.

       또한, if 혹은 while문과 결합하여 사용할 수 있다.

var dog: String? = "pitbull"

//옵셔널 바인딩을 통한 임시 상수 할당.(nil이 아니면 true) 
it let puppy = dog{
   print("dog is \(puppy)")
}else{
   print("dog is nil")
}
// dog is puppy

//puppy라는 임시 상수는, if블록 내에서만 사용할 수 있다.
//따라서 별도로 사용하는 것도 가능하다.


//한번에 여러 옵셔널 값을 추출할 때는 쉼표(,)를 이용한다.
var cat: String? = "persian"
var kitty: String? = nil

if let myCat = cat, let myKitty = kitty{
  print("they are my \(cat) and \(kitty)")
}
//kitty가 nil을 할당받아 바인딩이 되지 않았으므로 실행되지 않는다.

      

   - 암시적 추출 옵셔널(Implicity Unwrapped Optionals)

      타입 뒤에 느낌표(!)를 사용해주면 된다.

      지정된 타입은 일반 값처럼 사용할 수 있으나, 옵셔널이기 때문에 nil을 할당해줄 수 있다.

      → 그러나, nil이 할당되어 있을 때 접근을 시도하면 런타임 오류가 발생한다.

//타입 뒤에 느낌표를 붙여 암시적 추출 옵셔널을 한다.
var cat: String! = "russianBlue"

//옵셔널이기 때문에 nil할당이 가능하다.(바인딩도 사용 가능)
cat = nil

//nil이 할당되어 있을 때 사용하면 오류가 발생한다
print(cat.count) //Error

 

📌  옵셔널을 사용할 때는,

강제추출 / 암시적 추출 옵셔널 사용을 지양하고,

옵셔널 바인딩, 옵셔널 체이닝 등의 방법을 사용하는 것이 훨씬 안전하며, Swift의 지향점과도 부합한다고 볼 수 있다.

 

 

4. 심화 - 옵셔널 뜯어보기

옵셔널은 제네릭이 적용된 열거형이라고 한다. 옵셔널의 정의를 보자.

public enum Optional<Wrapped>: ExpressibleByNilLiteral{
  case none
  case some
  public init(_ some: Wrapped)
  //중략...
}

주목해야 할 점은, none과 some이다.

값이 nil이면 none이고, 값이 있으면 some이다.

이 때 옵셔널에 값이 있으면, some의 연관 값인 Wrapped에 값이 할당된다고 한다.

즉 값이 옵셔널이라는 열거형에 Wrapping되어있는 것이다.

 

옵셔널이 열거형이라 switch구문을 통해서도 값을 확인할 수 있다!

switch optionalValue{
  case .none:
    print("Optional is nil")
  case .some(let value):
    print("Optional is \(value)")
}

옵셔널 마다 일일이 switch를 적용하는 것은 비효율적이므로 위에서 서술한 옵셔널 바인딩 등의 값 추출 방법을 이용하는 것이다.

 

 

 

오류 및 지적사항은 댓글로 남겨주시면 감사하겠습니다!

참고 : 스위프트 프로그래밍 3판(한빛미디어)

Comments