본문 바로가기
iOS/Swift

Swift) 값이 없는 듯 있는 nil의 정체 (feat. 옵셔널은 열거형이다)

by Dev.Andy 2023. 5. 9.

목차

    머리말

    옵셔널을 공부하던 중 알게 된 nil의 정체

    Optional에 대한 강의를 듣다가 아주 충격적인(?) 말을 듣게 되었다.

    Swift의 nil은 C언어의 null처럼 단순히 값이 없는 상태가 아니라, 값이 없음을 나타내는 임시적인 값이다.

     

    나는 nil이라는 값이 정말 존재하는 줄 알았는데 그건 아니고 임시 값이라는 것을 알게 되었다.

    이게 뭔 소리람… 값이 있는 데 없는 척을 한다는 건가

    nil의 개념에 대해 찾게 된 과정

    1) Swift 공식 문서에서의 nil

    공식 문서에서 nil에 대한 내용은 Objective-C에서의 nil과 Swift에서의 nil을 비교하는 내용에 적혀 있는데, 직접 번역해 보았다.

    1-1) 값의 부재(不在)

    (2023-12-02 검토) 

    In Objective-C, `nil` is a pointer to a nonexistent object. In Swift, `nil` isn’t a pointer — it’s the absence of a value of a certain type. Optionals of any type can be set to nil, not just object types.

    Swift의 nil은 Objective-C의 `nil`과는 다른데, Objective-C의 `nil`은 존재하지 않는 객체에 대한 포인터이다. (반면에) Swift의 nil은 포인터가 아니다. 이것(nil)은 어느 특정 타입에서 하나의 값의 부재(不在; absence)다. 옵셔널에 대한 어떠한 타입도 nil로 설정될 수 있으며, 단순한 객체 타입이 아니다.

    nil - The Basics | Documentation

    • 여기서 눈여겨 봐야 하는 점은 nil은 특정 타입에서 값이 없음(the absence of a value of a certain type)을 나타내는 것이다.
    • 평소에 궁금하면 바로 찾아본 공식 문서에서는 nil에 대한 내용이 분명 있었지만, 깔끔하게 해결된 기분이 아니었다. 값의 부재가 무엇인지에 대해서는 자세히 나와 있지 않아 아쉬웠다.

    1+) 포인터(pointer)?

    (2023-12-02 추가)

    특정 값을 가리키는 시작 주소 (변수 & 주솟값)

    포인터는 특정 값이 아닌 어느 변수의 주솟값을 데이터로 담아, 해당 변수의 시작 주소를 가리킨다.
    포인터는 특정 값이 아닌 어느 변수의 주솟값을 데이터로 담아, 해당 변수의 시작 주소를 가리킨다.

    int number = 10;
    int *ptr = &number; // number의 시작 주소를 가리킴
    // ptr은 number을 참조, number은 ptr을 역참조

    개념

    • 그렇다면 공식 문서에서 말하는 포인터(pointer)는 무엇일까?
    • 포인터는 C 언어에서 중요한 개념 중 하나이다.
    • 데이터의 공간(메모리)에 실제 값이 아닌 어떠한 변수의 주솟값(address value)을 할당하여, 위치만을 알려주는 것을 말한다.

    개념+

    • 정확히는, 해당 값이 실제로 저장된 주소 중 "시작 주소"를 알려준다.
    • 또한, 포인터는 실제 주솟값을 상수가 아닌 변수로 할당한다. 

    연산자 및 NULL

    • 앰퍼샌드(ampersand; `&`)를 이용해 참조된(pointed) 대상의 주솟값을(reference; 참조) 알려주고,
    •  애스터리스크(asterisk; `*`)를 이용해 참조한(pointing) 대상의 실제 데이터 값(dereference; 역참조)을알려준다.
    • 📌 따라서, 포인터에 nil (C 언어에서는 NULL)이라면, 가리키는 주소를 할당하지 않는다는 것을 의미한다. (== 아무것도 가리키지 않는다)

    Swift의 참조 타입과 클래스

    • 클래스가 참조 타입이고 구조체가 값 타입이라고 하는데,
    • 여기서 클래스에 포인터의 개념이 녹아 있다고 생각하면 된다.

    2. 스터디원과의 모각코 시간과 오픈 소스(open source)

    게더타운에서 스터디원끼리 서로 얘기하는 이미지
    스터디원과의 모각코 타임

    요즈음 게더타운에서 평일 2~3시마다 여건이 되는 스터디 조원들끼리 모각코를 하는데, 강의에서 배운 nil의 내용과 나의 궁금증을 설명했다. 그러다 문득 "Swift는 오픈 소스이니까 당연히 GitHub에 소스 코드가 있지 않을까"하는 생각이 들었다.

    3. 처음 접하는 오픈 소스와 너무 많은 디렉토리들…

    Swift에 대해 리포지토리를 처음 들어갔는데 당연히도 너무 많은 디렉토리와 파일의 양에 찾기가 너무 힘들었다.

    3. GitHub에서 nil에 대한 Swift 오픈 소스를 찾을 수는 없을까?

    Swift의 GitHub repository 이미지
    Swift의 GitHub repository... 여기서 nil을 어떻게 찾죠...?

    그러다 문득 "'ChatGPT'로 한번 오픈 소스를 검색을 해볼까?" 라는 생각이 들었다.

    4. ChatGPT로 nil에 대한 Swift 오픈 소스 위치를 검색… 해결!

    🔗 `nil`에 대한 GitHub 오픈 소스 링크 → swift/Optional.swift at main · apple/swift

    ChatGPT로 검색해 본 nil의 오픈 소스 이미지
    ChatGPT로 검색해 본 nil의 오픈 소스

    너무 궁금해서 ChatGPT로 nil에 대한 Swift검색을 해 보았는데, 아주 친절하게 코드와 링크까지 알려줬다.

    ⭐️ Optional 타입은 두 cases로 구성된 열거형(Enumeration)이다.

    (2023-12-02 검토)

    GitHub 소스 코드 - (1) 각주

    • 각주 1~2번째 줄을 보면 "'Optional.none'은 'nil' 리터럴과 같다."라는 내용이 있다.
    • 📌 즉, 두 cases로 구성된 Optional에서 `.none`에 해당하는 값이 바로 `nil`이다.

    아래의 코드에 적힌 글을 살펴 보자.

    /// 📌 바로 여기 Optional 타입은 두 cases로 구성된 열거형이다.
    /// The `Optional` type is an enumeration with two cases. `Optional.none` is
    /// equivalent to the `nil` literal. `Optional.some(Wrapped)` stores a wrapped
    /// value. For example:
    ///
    ///     let number: Int? = Optional.some(42)
    ///     let noNumber: Int? = Optional.none
    ///     print(noNumber == nil)
    ///     // Prints "true"
    The `Optional` type is an enumeration with two cases. `Optional.none` is equivalent to the `nil` literal. `Optional.some(Wrapped)` stores a wrapped value.
    옵셔널 타입은 두 개의 케이스로 구성된 열거형(enumeration)이다. `Optional.none`은 `nil` 리터럴과 동일한다. `Optional.some(Wrapped)`은 포장된 값을 저장한다.

    ⭐️ Optional의 열거형 중 `none`이 바로 `nil`이다.

    GitHub 소스 코드 - (2) 실제 코드

    실제 nil의 코드를 보면 Optional<Wrapped>에 대한 enum(열거형) 중 `none`의 경우를 나타낸다.

    @frozen
    public enum Optional<Wrapped>: ExpressibleByNilLiteral {
      // The compiler has special knowledge of Optional<Wrapped>, including the fact
      // that it is an `enum` with cases named `none` and `some`.
    
      /// The absence of a value.
      ///
      /// In code, the absence of a value is typically written using the `nil`
      /// literal rather than the explicit `.none` enumeration case.
      case none
    
      /// The presence of a value, stored as `Wrapped`.
      case some(Wrapped)
    
      /// Creates an instance that stores the given value.
      @_transparent
      public init(_ some: Wrapped) { self = .some(some) }
    
      /// Evaluates the given closure when this `Optional` instance is not `nil`,
      /// passing the unwrapped value as a parameter.
      ///
      /// Use the `map` method with a closure that returns a non-optional value.
      /// This example performs an arithmetic operation on an
      /// optional integer.
      ///
      ///     let possibleNumber: Int? = Int("42")
      ///     let possibleSquare = possibleNumber.map { $0 * $0 }
      ///     print(possibleSquare)
      ///     // Prints "Optional(1764)"
      ///
      ///     let noNumber: Int? = nil
      ///     let noSquare = noNumber.map { $0 * $0 }
      ///     print(noSquare)
      ///     // Prints "nil"
      ///
      /// - Parameter transform: A closure that takes the unwrapped value
      ///   of the instance.
      /// - Returns: The result of the given closure. If this instance is `nil`,
      ///   returns `nil`.

    애플 개발자 공식 문서

    또 다시 언급 되는 값의 부재(不在)

    실제 애플 개발자 공식 문서에도 Optional.none이 있는데 앞에 살펴 본 nil의 정의와 똑같이 "값의 부재(the absence of a value)"라 적혀 있다.

    Optional.none | Apple Developer Documentation

    열거형 케이스보다는 `nil`이라는 키워드 사용하기 (권장)

    또한 위의 공식 문서의 Overview에서 언급 하기를, nil이라는 리터럴(즉, 키워드)을 사용하여 값의 부재를 표현하도록 권장하고 있다.

    In code, the absence of a value is typically written using the nil literal rather than the explicit .none enumeration case.
    코드에서, `.none`이라는 열거형 케이스를 직접 쓰는 것보다는 일반적으로 `nil` 리터럴을 사용하여 값의 부재를 표현한다.

    꼬리말

    여러 소스로부터의 도움

    입체적인 학습의 필요성

    • 공식 문서와 스터디원과의 의논, ChatGPT… 여러 과정을 거치면서 해당 내용을 탐구하고 살펴 보았다.
    • 어떠한 것을 공부할 때 이렇게 여러 방향으로 학습하는 것이, 결국 해당 개념을 입체적으로 학습할 수 있는 좋은 경험이자 기회가 되었다.

    앞으로 이해하기 위해 남은 것들

    열거형과 오픈소스

    1. 아직 enum에 대해 자세히 배우지는 못했지만 확실히 nil이 (임시) 값이 있다는 것은 확실히 알게 되었다.
    2. Swift를 GitHub의 오픈소스로 공부하는 것은 처음이었는데, 앞으로도 적극 활용해야겠다!

    심화 학습과 복습

    공식 문서의 중요성

    1. 포인터의 개념에 대해 살펴보고, 옵셔널 관련 공식 문서를 다시 읽어 보았는데, 많은 도움이 되었다.
    2. 공식 문서를 꼼꼼히 살펴 보는 습관이 정말 중요한 것을 다시 인식하게 되었다.

     

    수정일 2023-12-02 토

    영문 아래 적힌 한글 번역은 필자가 번역하였습니다.

    댓글