Search
Duplicate

Optional Chaining (옵셔널 체이닝)

생성일
2023/08/04 07:37
태그
Grammar

 Optional Chaining (옵셔널 체이닝)

→ 옵셔널을 연쇄적으로 사용하는 것
옵셔널 체이닝은 옵셔널에 속해 있는 nil 일지도 모르는 프로퍼티, 메서드, 서브스크립션 등을 가져오거나 호출할 때 사용할 수 있는 일련의 과정이다.
옵셔널이 nil 이라면 프로퍼티, 메서드, 서브스크립트 등은 nil로 반환한다.
즉, 옵셔널을 반복 사용하여 옵셔널이 자전거 체인처럼 서로 꼬리를 물고 있는 모양이기 때문에 옵셔널 체이닝이라고 부른다.
중첩된 옵셔널 중 하나라도 값이 존재하지 않는다면 결과적으로 nil 이 반환된다.
옵셔널 체이닝은 프로퍼티나 메서드 또는 서브스크립트를 호출하고 싶은 옵셔널 변수나 상수 뒤에 물음표(?) 를 붙여 표현한다.
옵셔널이 nil 이 아니라면 정상적으로 호출될 것이고, nil 이라면 결과값으로 nil 으로 반환할 것이다.
결과적으로 nil 이 반환될 가능성이 있으므로 옵셔널 체이닝의 반환된 값은 항상 옵셔널이다.
// 호실 class Room { // 호실 번호 var number: Int init(number: Int) { self.number = number } } // 건물 class Building { // 건물 이름 var name: String // 호실 정보 var room: Room? init(name: String) { self.name = name } } // 주소 struct Address { var province: String // 광역시/도 var city: String // 시/군/구 var street: String // 도로명 var building: Building? // 건물 var detailAddress: String? // 건물 외 상세주소 } // 사람 class Person { var name: String // 이름 var address: Address? // 주소 init(name: String) { self.name = name } }
Swift
복사
let hj: Person = Person(name: "hj") let hjRoomViaOptionalChaining: Int? = hj.address?.building?.room?.number // nil let hjRoomViaOptionalChaining: Int? = hj.address!.building!.room!.number // 오류 발생
Swift
복사
옵셔널 바인딩 사용
let hj: Person = Person(name: "hj") var roomNumber: Int? = nil if let hjAddress: Address = hj.address { if let hjBuilding: Building = hjAddress.building { if let hjRoom: Room = hjBuilding.room { roomNumber = hjRoom.number } } } if let number: Int = roomNumber { print(number) } else { print("Can not find room number") }
Swift
복사
위의 내용을 옵셔널 바인딩 + 옵셔널 체이닝을 사용하여 구현
let hj: Person = Person(name: "hj") if let roomNumber: Int = hj.address?.building?.room?.number { print(roomNumber) } else { print("Can not find room number") }
Swift
복사
이처럼 옵셔널 체이닝을 통해 한 단계 뿐만 아니라 여러 단계로 복잡하게 중첩된 옵셔널 프로퍼티나 메서드 등에 매번 nil 체크를 하지 않아도 손쉽게 접근할 수 있다.
또한 옵셔널 체이닝을 통해 값을 받아오기만 하는 것이 아니라, 반대로 값을 할당해줄 수도 있다.
hj.address?.building?.room?.number = 505 print(hj.address?.building?.room?.number) // nil // 아직 hj의 address 프로퍼티가 없으므로 nil
Swift
복사

옵셔널 체이닝을 통한 값 할당

hj.address = Address(province: "대구광역시", city: "수성구", street: "달구벌대로", building: nil, detailAddress: nil) hj.address?.building = Building(name: "방구석") hj.address?.building?.room = Room(number: 0) hj.address?.building?.room?.number) = 505 print(hj.address?.building?.room?.number) // Optional(505)
Swift
복사
옵셔널 체인에 존재하는 프로퍼티를 실제로 할당해준 후 옵셔널 체이닝을 통해 값이 정상적으로 반환되는 것을 확인할 수 있다.
옵셔널 체이닝을 통해 메서드와 서브스크립트 호출도 가능하다.
서브스크립트는 인덱스를 통해 값을 넣고 빼올 수 있는 기능이다.

옵셔널 체이닝을 통한 메서드 호출

호출 방법은 프로퍼티 호출과 동일하다. 만약 메서드의 반환 타입이 옵셔널이라면 이 또한 옵셔널 체인에서 사용 가능하다.
// 주소 struct Address { var province: String // 광역시/도 var city: String // 시/군/구 var street: String // 도로명 var building: Building? // 건물 var detailAddress: String? // 건물 외 상세주소 init(province: String, city: String, street: String) { self.province = province self.city = city self.street = street } func fullAddress() -> String? { var restAddress: String? = nil if let buildingInfo: Building = self.building { restAddress = buildingInfo.name } else if let detail = self.detailAddress { restAddress = detail } if let rest: String = restAddress { var fullAddress: String = self.province fullAddress += " " + self.city fullAddress += " " + self.street fullAddress += " " + rest return fullAddress } else { return nil } } func printAddress() { if let address: String = self.fullAddress() { print(address) } } } hj.address?.fullAddress()?.isEmpty() // false hj.address?.printAddress() // 대구광역시 수성구 달구벌대로 방구석
Swift
복사
서브스크립트를 가장 많이 사용하는 곳은 Array와 Dictionary다.
옵셔널의 서브스크립트를 사용하고자 할 때는 대괄호([])보다 앞에 물음표(?) 를 표기해주어야 한다.
이는 서브스크립트 외에도 언제나 옵셔널 체이닝을 사용할 때의 규칙이다.
let optionalArray: [Int]? = [1, 2, 3] optionalArray?[1] // 2 var optionalDictionary: [String: [Int]]? = [String: [Int]]() optioanlDictionary?["numberArray"] = optionalArray optioanlDictionary?["numberArray"]?[2] // 3
Swift
복사

 옵셔널 체이닝의 특징

 옵셔널 체이닝의 결과값의 타입은 마지막 표현식의 옵셔널 타입이다.

왜 반환값이 옵셔널이 붙는 걸까?
→ 만약 email에 닿기전에 sodeul이 nil이면 그냥 nil을 뱉어버리기 때문이다.
→ 이러한 이유로 옵셔널 체이닝의 반환값은 무조건 옵셔널 타입!

 옵셔널 체이닝의 마지막 표현식은 옵셔널이더라도 ?를 생략한다.

struct Contacts { var email: String? var address: [String: String] }
Swift
복사
이렇게 email의 변수를 옵셔널 String으로 바꿔주었다.
> 그럼 옵셔널이니까 접근할 때 옵셔널 체이닝에 의해 ?를 써줘야 하나??
let email = hj?.contacts.email? // '?' must be followed by a call, member lookup,
Swift
복사
→ 에러가 난다.
 옵셔널 체이닝의 마지막 표현식은 옵셔널이든 아니든 ? 를 생략한다!

 옵셔널 체이닝의 표현식 중 하나라도 nil이라면, 이어지는 표현식은 평가하지 않고 nil을 리턴한다.

 Reference