Optional에 대하여

이원희·2020년 11월 6일
2

 🐧 Swift

목록 보기
2/32
post-thumbnail

오늘은 Optional에 대해서 정리해보자.
이전에도 정리했지만 재밌는게 있어서 다시 한 번 정리하고 넘어간다.


Optional이란?

공식 문서를 확인해 보자.

A type that represents either a wrapped value or nil, the absence of a value.

값 혹은 nil 을 감싸고 있는 타입이며 nil값의 부재를 의미한다.

그럼 이렇게 정리할 수 있을거 같다.

Optional은 값이 있을 수도 없을 수도 있는 경우를 표현하는 타입이다.


Optional은 어떻게 생겼을까?

Optional은 enumGenerics로 이뤄져 있다.

이 부분은 아래에서 다시 살펴보도록 하고 지금은 enumGenerics로 구현되어 있다는 것만 알고 넘어가자.


Optional은 어떻게 사용할까?

Optional을 사용해보자.

let longForm: Optional<Int> = Int("42")
let shortForm: Int? = Int("42")

longForm을 보면 Optional<Int> 로 타입을 정의하고 있다.

Optional은 Generics로 구현되어 있으므로 Optional의 GenericsInt라는 타입을 사용하겠다고 알려주고 있다.

하지만 타입을 정의하는 부분이 길고, 안 이쁘다.🤔

Swift type 시스템은 Optional<Int> 처럼 전체 type을 표시하는 대신 사용할 type 뒤에 ? 을 사용해 Optional type을 정의하도록 지원한다.

Swift type 시스템의 지원으로 shortForm의 type 정의가 가능해졌다.

훨씬 읽기 쉽고 짧아졌다.👏


Optional enum

enum Optional<Wrapped> {
  case none
  case some(Wrapped)
}

Optional은 none, some case로 구성된 enum이다.

.nonenil 과 동일하다.
.some래핑된 값을 저장한다.

let number: Int? = Optional.some(42)
let noNumber: Int? = Optional.none
print(noNumber == nil)

number에는 Optional.som(42)로 42를 래핑해 저장할거라고 알려준다.
noNumberOptional.none으로 nil을 저장한다.


Optional 안전하게 사용하기

Optional은 값이 있을 수도 없을 수도 있는 타입이다.

Swift는 해당 타입을 안전하게 사용하기 위해 다양한 unWrapping 방법을 제공하고 있다.

Optional Binding

Optional 인스턴스의 래핑된 값을 새로운 값으로 unWrapping하기 위해 if let, guard let, switch를 사용할 수 있다.

let number: Int? = 5

if let bindingNumber = number {
  print("This is binding number: \(bindingNumber)")
} else {
  print("This is nil")
}

guard let bindingNumber = number else {
  print("This is nil")
  return
}
print("This is binding number: \(bindingNumber)")

switch number {
case .none:
    print("This is nil")
case .some(let bindingNumber):
    print("This is binding number: \(bindingNumber)")
}

Optional은 enum으로 구현되어 있으므로 .none.some case를 switch문으로 분기할 수 있다.


Optional Chaining

unWrapping된 property와 method에 안전하게 접근하기 위해 Optional Chaining 연산자 ?을 사용할 수 있다.

let str: String? = "abcd"
if str?.hasPrefix("a") == true {
    print("yes")
} else {
    print("no")
}

let str2: String? = nil
if str2?.hasPrefix("a") == true {
    print("yes")
} else {
    print("no")
}
if str2?.hasPrefix("a") == nil {
    print("nil")
}

Optional Chaining은 래핑된 값을 그대로 사용하고 chain에 nil이 존재한다면 nil을 반환한다.

str?.hasPrefix()를 보면 ?로 Optional Chaining이 사용된 것을 알 수 있다.

🤔그렇다면 str2처럼 nil이라면 어떤 결과가 나올까?

if문을 통해 확인해보자.

정답은 nil이 출력된다.

Optional Chaining은 래핑된 값을 그대로 사용하므로 str2nil을 가지고 있다.

chain에 nil이 존재하므로 결과로 nil이 반환된다.


Using the Nil-Coalescing Operator

?? 연산자를 사용하면 Optional type에 기본값을 지정해줄 수 있다.

let number: Int? = nil
let basicNumber = number ?? 0
print(basicNumber)

numbernil이므로 ?? 연산자로 정해준 기본 값인 0이 basicNumber로 지정된다.


Unconditional Unwrapping

Optional에 값이 있다고 확신할 수 있는 경우 ! 연산자를 사용해 강제 언래핑 할 수 있다.

let number: Int? = 5
print(number!)

let numberNil: Int? = nil
print(numberNil!)

number는 Optional이지만 값을 가지고 있다.

number는 5라는 값을 가지고 있으므로 !를 사용해 강제 언래핑 할 수 있다.

numberNilnil이다.

numberNil을 강제 언래핑하면 어떻게 될까?

nil을 강제 언래핑하면 런타임에 error를 마주할 수 있다.

❗️ 그러므로 꼭 값이 있다고 확신할 수 있는 경우에만 사용하자.


⁉️

! 연산자를 통해 강제 언래핑을 알아봤다.

let number: Int! = 1

위에서 Int!는 무엇일까?

강제 언래핑이라고 하기엔 이상하다.

사실 Int!와 같은 형태는 iOS를 공부하면서 아래와 같은 형태로 많이 접한다.

@IBOutlet weak var button: UIButton!

Int? Int!

Int?, Int!에 대해 알아보자.

Int?는 Optional type임을 우리는 알고 있다.
그렇다면 Int!는 무엇일까?🤔

var valueOne: Int? = 1
var valueTwo: Int! = 1

valueOne = nil
valueTwo = nil

위의 코드를 실행해보면 error 없이 실행된다.

즉, Int?, Int! 모두 nil을 할당할 수 있다.

🤔 흠... 그런데 위와 같이 String! 타입인 str을 선언하고 nil을 할당했다.
그리고 str method를 사용하려고 했는데 error가 나왔다.

unexpectedly found nil while implicitly unwrapping an optional value

"암시적 언래핑에서 예기치 못하게 nil이 발견되었다"라고 한다.

Int!와 같은 형태를 암시적 언래핑이라고 한다.

암시적 언래핑 타입은 nil을 지정할 수는 있지만 nil일때 접근하면 위와 같은 error를 받는다.


@IBOutlet weak var button: UIButton! 구문을 뜯어볼까

@IBOutlet weak var button: UIButton!

위의 코드는 iOS 프로젝트를 진행하다보면 정말 많이 보게되는 코드이다.

weak에 대해서도 할 말이 많지만 다음으로 미뤄두고!
UIButton!에 대해 생각해보자.

우선, Swift의 클래스는 안전을 위해 프로퍼티가 세팅되기 전까지는 사용할 수 없다.

이는 클래스를 초기화할때 프로퍼티에 초기 값이 없으면 초기화가 불가능해 클래스의 프로퍼티에 nil이 입력된다.

즉, !를 붙여두면 nil 상태지만 run되면서 스토리보드와 연결 되는 것이다.
(!은 nil값을 지정할 수 있으므로)

음.. 나도 아직 빡!!하고 느낌은 안 오지만 내가 이해한 바로는 다음과 같다.

  • controller는 class이다
  • class가 초기화하기 위해서는 프로퍼티의 초기 값이 필요하다
  • controller에는 UI 요소들을 정의한 프로퍼티가 있고, 이들의 초기 값이 필요하다
  • view가 로드되지 않으면 UI 요소들을 알 방법이 없다
  • view의 요소들이 메모리에 다 올라간 후인 viewDidLoad부터는 nil이 아니게 된다.

그리고 처음에는 nil였다 viewDidLoad 이후부터 값이 변하므로 var와 함께 선언되어야 한다.

(틀린 부분이 있다면 알려주면 너무너무 고마울거 같다🙇‍)


마무리

오늘은 Optional에 대해 알아봤다.
Optional 코드가 궁금해서 이어서 code를 뜯어보려고 한다.
궁금하다면 아래 링크로...!
그럼 이만👋

Optional Code

2개의 댓글

comment-user-thumbnail
2020년 12월 30일

UIButton!과 같은 forced unwrapping optional은 스토리보드와 연결될 때 nil이 아닌 값이 되지 않습니다.
뷰컨트롤러가 viewDidLoad가 되면 뷰 객체들이 생성되면서 nil이 아니게 됩니다.
참고 : http://minsone.github.io/mac/ios/iboutlet-forced-unwrapping-vs-optional

1개의 답글