옵셔널 체이닝은 현재 nil일 수 잇는 옵셔널인 프로퍼티, 메서드 및 서브스크립트를 조회하고 호출하기 위한 프로세스이다. 옵셔널에 값이 있는 경우엔 프로퍼티 및 메서드 등은 호출에 성공한다. 하지만 옵셔널이 nil인 경우엔 프로터피, 메서드 및 서브스크립트 호출은 nil을 반환한다. 여러 조회는 한 줄로 연결 가능하며, 체인에 어느 부분이라도 nil인 경우 전체 체인은 실패한다.
class Person {
var residence : Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = person()
//현재 존은 nil의 residence 프로퍼티 값을 가지고 있다.
//아래서는 이에 대해 강제 언래핑을 시도하는 코드를 작성한다
let roomCount = john.residence!.numberOfRooms
//다음과 같을 시 에러가 난다. 왜냐면 레지던스 값이 nil로 없는 상태이기 때문이다.
if let roomCount = john.residence?.numberOfRooms {
print(roomCount)
} else {
print("unable to retrieve roomCount")
}
//옵셔널 체이닝은 numberOfRoom에 접근하기 위한 대안으로 제공된다. 체이닝을 위해서 느낌표 위에 물음표를 사용해준다.
//이것은 옵셔널 residence 프로퍼티를 "체인" 하고 residence가 존재 시 numberOfRooms 값을 조회하도록 설정한 것이다.
class Person {
var residence : Residence?
}
class Residence {
var rooms : [Room] = []
var numberOfRooms : Int {
return rooms.count
}
func printNumberOfRooms() {
print(numberOfRooms)
}
var address : Address?
}
class Room {
let name : String
init(name : String) {
self.name = name
}
}
class Address {
var building : String?
var buildingNumber : String?
var street : String?
func buildingIdentifier() -> String? {
if let number = buildingNumber, let street = street {
return "\(number), \(street)"
else {
return nil
}
}
}
//다음과 같이 각 옵셔널 인스턴스를 포함하는 4개의 클래스를 정의하였다.
//아래에서는 옵셔널 체이닝을 통해 프로퍼티에 접근해본다.
let john = Person()
if let roomCount = john.residence?.numberOfRooms {
print("nice!")
} else {
print("unable to do that")
}
//다음에서 john의 residence는 현재 nil로 초기화 되어있으므로 nil을 리턴하여 else를 수행하게 된다.
let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
john.residence?.address = someAddress
//여기에서는 아직 john의 residence가 nil이기 때문에 address에 접근하기 위한 시도는 에러가 난다.
if john.residence?.printNumberOfRooms() != nil {
print("possible to print")
} else {
print("impossible to print")
}
//다음과 같이 옵셔널 체이닝을 통해서 메서드에도 접근이 가능하며, 체이닝 결과에 따라서 흐름제어가 가능하다.
if let firstRoomName = john.residence?[0].name {
print(firstRoomName)
} else {
print("no name!")
}
//다음과 같이 서브스크립트에도 접근이 가능하다. 지금은 residence 인스턴스가 가지는 배열의 값에 대해
//index를 통해 접근하는 시도를 하고 있고,
//이 또한 만약 residence가 nil인 경우 전체 nil을 반환하여 호출은 실패한다.
if let johnStreet = john.residence?.address?.street {
print(johnStreet)
} else {
print("unable to do that")
}
//다음과 같이 2중 옵셔널 체이닝이 가능하며, 단계적으로 옵셔널 여부를 체크하며
//코드를 전개할 수 있다.
if let buildingIdentifier` = john.residence?.address?.buildingIdentifier() {
print(buildingIdentifier)
} else {
print("can't")
}