3.2 Optionals

Joohyun·2022년 4월 13일
0

Optional (?)

  • Optional은 2가지 가능성을 표현한다.

    1. 사용할 수 있는 값이 존재하거나

    2. 존재하지 않거나 (nil)

  • ?를 통해 optional type을 생성한다.

// 아직 출판되지 않은 책은 출판년도가 존재하지 않는다.
struct Book {
  var name: String // 책 제목
  var publicationYear: Int? // 출판년도 (Int optional)
}
let firstDickens = Book(name: "A Christmas Carol", publicationYear: 1843)

// 출판년도: nil
let unannouncedBook = Book(name:Rebels and Lions, publicationYear: nil)
  • optional을 생성할 때에는 반드시 type을 구체화해주어야 한다.
// Error
var serverResponseCode = nil

// 404로 설정했지만, 나중에 nil일 수도 있음
var serverResponseCode: Int? = 404
 
// nil로 설정했지만 나중에 Int값이 올 수 있음
var serverResponseCode: Int? = nil

Force-Unwrap Operator (!)

  • optional 값에 접근할 경우 nil값이 아님을 확인한 후에 !를 통해 값을 추출할 수 있다.
if publicationYear != nil {
  let actualYear = publicationYear!
  print(actualYear)
}
  • nil값이 아님을 확인하지 않고 nil값에 !를 사용하면 런타임 에러가 발생한다.
let unwrappedYear = publicationYear! // runtime error
print(unwrappedNumber)

- Safety & Clarity
optional 값에 접근하기 전 nil 여부를 확인하는 것은 매우 중요!

Optional binding

  • optional에 값이 존재할 경우 non-optional type의 값을 새로운 상수에 저장한다.
// publicationYear 값이 존재할 경우에만 unwrappedPublicationYear에 값을 할당하고 내부 로직 실행
if let unwrappedPublicationYear = book.publicationYear {
  print(The book was published in \(unwrappedPublicationYear))
}
else {
  print(The book does not have an official publication date.)
}

Function with Optional

1. Return optional value

  • Int initializer는 Int? type을 반환한다.
// parameter인 String이 Int로 변경될 수 있다면 Int값을 반환
let string =123let possibleNumber = Int(string)
// Int로 변경될 수 없다면 nil을 반환
let string =Cynthialet possibleNumber = Int(string)
// URL값에 해당하는 웹사이트의 텍스트를 반환하는 함수
// URL값에 해당하는 웹사이트가 존재하지 않거나, 해당 사이트에 아무 텍스트가 존재하지 않을 수 있기 때문에 optional 반환
func textFromURL(url: URL) -> String?

2. optional parameter

  • 함수의 parameter에 optional이 올 수 있음
// middle name이 존재하지 않는 사람도 있으므로 middleName에 nil값이 올 수 있음
func printFullName(firstName: String, middleName: String?, lastName: String)

Failable Initializers

  • optional type의 instance를 반환하는 initializer를 뜻한다.
struct Toddler {
  var name: String
  var monthsOld: Int
 
  // Failable Initializer
  // Toddler? type의 instance 반환
  init?(name: String, monthsOld: Int) {
    // nil: 12개월보다 작거나 36개월보다 큰 경우
    if monthsOld < 12 || monthsOld > 36 {
      return nil
    } else {
      self.name = name
      self.monthsOld = monthsOld
    }
  }
}
  • failable initializer는 optional형식의 instance를 생성하기 때문에 instance를 사용하기 전 optional binding을 통해 값을 추출할 수 있다.
let toddler = Toddler(name:Joanna, monthsOld: 14)
 
if let myToddler = toddler {
  print(”\(myToddler.name) is \(myToddler.monthsOld) months old”)
} else {
  print(The age you specified for the toddler is not between 1 
  and 3 yrs of age”)
}

Optional Chaining

nested optionals

  • optional값이 optional property를 같는 것
struct Person {
  var age: Int
  var residence: Residence? // 주거지가 없는 사람도 존재
}
 
struct Residence {
  var address: Address? // 거주지를 주소로 표현할 수 없을 수 있다.
}
 
struct Address {
  var buildingNumber: String
  var streetName: String
  var apartmentNumber: String? // 모든 주소가 아파트번호를 가지고 있지는 않는다.
}

optional chaining

  • ?을 통해 선택적으로 값을 추출할 수 있다.

  • nil값이 존재하면 constant에 아무 값도 할당되지 않은 채 if let문을 빠져나온다.

// 거주지가 존재하고, 주소로 표시가 가능하고, 아파트번호가 있는 경우에만 내부로직 수행
if let theApartmentNumber =
    person.residence?.address?.apartmentNumber {
  print(He/she lives in apartment number \(theApartmentNumber).)
}

Implicitly Unwrapped Optionals

  • 모든 non-optional property가 주어져야만 instance가 초기화될 수 있지만, 몇몇 property는 initialize 후에 잠시 nil값일 수 있다.

  • implicitly unwrapped optional은 값을 추출하기 위해 if let을 사용하지 않아도 된다.

  • 하지만, implicitly unwrapped optional의 값이 nil이면 프로그램이 충돌한다.
    -> 꼭 필요한 부분에만 사용해야한다.

예시) IBOutlet

class ViewController: UIViewController {
  @IBOutlet var label: UILabel!
}
  • ViewController가 초기화 된 후, 잠시후에 storyboard에 대응하는 outlet들이 연결된다.

  • label은 잠시동안 nil값을 갖는다.

  • 그 후에 label은 storyboard와 항상 연결되어 있는데, UILabel?로 표기한다면 매번 if-let을 써야하므로 비효율적

  • ? 대신 !(implicitly unwrapped optional)을 써서 해당 값을 사용할 때 자동으로 optional값에서 추출하여 넘겨준다.

profile
IOS Developer

0개의 댓글