Optional은 무엇이고 왜 필요할까?

박성민·2021년 1월 15일
0

iOS

목록 보기
4/30

Optional의 필요성

var name: String // 컴파일에러
var name = nil // 컴파일에러

만약 Optional(?) 키워드를 사용하지 않았다면 값을 입력하라는 에러가 발생하고, 그 이후에라도 nil을 넣으려하면 컴파일에러가 발생합니다.
Swift가 기본적으로 non-optional 입니다. nil에 대한 컴파일 에러를 통해 개발자는 nil에 대해 명확한 예외처리가 강제 되며, 런타임에 nil로 인한 문제를 컴파일 단계 에서 예방할 수 있습니다.

Optional

public enum Optional<Wrapped>: ExpressibleByNilLiteral {
     case none	// abscence of value
     case some(Wrapped)	// presence of a value, stored as `Wrapped`
}
  • Optional은 변수나 상수 등에 값이 있음이 보장되지 않을 때 사용하는 기능으로 안정성(safe)이 보장되는 방법 중 하나
  • enum으로 정의되어 있는 optional은 none(값 없음)과 some(값이 있음) 케이스가 있다.
  • 하지만 유효한 값이 존재하더라도 some의 연관값 Wrapped에 할당되어있기 때문에 이 값을 사용하기 위해서는 값을 추출해내야 한다.

< Objective-C 와 Swift의 Optional>
Objective-C의 경우 유효한 객체 가 없을 때 함수에서 nil 포인터를 반환할 수 있다. -> 참조형식에 대해서만 적용이 가능한 개념.
반면 Swift의 optional은 모든 타입에 적용이 가능해 Objective-C보다 표현성이 더 뛰어나다.

Optional의 값 추출 방식

  • 강제 추출(Forced Unwrapping/ Unconditional Unwrapping)
  • 옵셔널 바인딩 (Optional Binding)
  • 암시적 추출 옵셔널 (Implicitly Unwrapped Optional)

강제 추출 (Forced Unwrapping/ Unconditional Unwrapping)

  • Optional로 선언한 상수/변수에 값이 있음이 확실할 때 !를 통해 강제 추출 가능.
  • 강제 추출 시 반환 값이 nil이면 런타임 오류가 발생.
let a: Int? // 자동으로 nil 할당
print(a!) // Fatal error: Unexpectedly found nil while unwrapping an Optional value

옵셔널 바인딩 (Optional Binding)

  • Optional binding은 optional에 값이 있는지 확인하기 위해 사용하여 if 또는 while 구문 등과 결합하여 사용.
  • 값이 있다면 추출한 값을 scope 내에서만 사용 가능한 상수/변수로 할당하여 사용할 수 있게끔 만들 수 있다. 만약 값이 없다면 블록 내부 명령문은 실행되지 않음.
var address: String? = "Seoul, Korea"
if let addr = address {
   print(addr)
}
  • guard 문에서는 optional binding된 상수를 guard 구문 실행 후 지역 상수처럼 쓸 수 있다. 하지만 guard 문은 언제나 else와 함께 쓰여 guard 문보다 상위 코드 블록을 종료하는 코드를 작성해야 하므로 예외사항을 처리해야 할 때 사용하면 좋다.
// URLSession dataTask를 통해 data, response, error 받아서 data 처리
guard let data = data else {
   completion(.failure(.APIInvalidResponse))
   return
}

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

프로젝트 구조/프로그램의 로직 상 optional이 무조건 값이 있다면 optional binding을 통해 추출하는 방식이 번거로울 것이다.
이 때 optional로 만들고자 하는 변수나 상수의 타입 뒤에 ? 대신 !를 붙여 implicitly unwrapped optionals를 사용하면 된다.
암시적 추출 옵셔널을 주로 볼 수 있는 경우는 바로 참조 순환을 피하기 위한 class를 초기화할 때 이다.

class Country {
    let name: String
    var capitalCity: City!
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
}

class City {
    let name: String
    unowned let country: Country
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
}

Optional Chaining

Optional chaining은 현재 nil일 수도 있는 properties, methods 와 subscripts를 호출하거나 값을 가져올 때 사용하는 방법으로 optional을 반복적으로 사용하여 중첩시킨다. Optional chaining을 통한 접근 역시 optional을 반환하므로 추출 방식 중 하나를 선택해서 진행해야한다.

let person: Person = Person(name: "Sueaty")
if let roomNumber: Int = person.address?.room?.number {
   print("Sueaty stays in room: #\(roomNumber)")
} else {
   print("Cannot find room number")
}

참고 및 출처

profile
iOS시작~

0개의 댓글