옵셔널 체이닝은 현재 널 값일 수도 있는 옵셔널에 프로퍼티, 메소드, 서브스크립트를 쿼리하거나 호출하는 과정이다. 옵셔널이 값을 가지고 있다면 프로퍼티, 메소드, 서브스크립트 호출이 성공한다. 값이 없다면 (즉 널 값이라면) 널 값을 리턴해 호출이 실패했음을 보여준다. 여러 개의 쿼리가 함께 묶일 수 있고, 쿼리 중 하나가 실패한다면 모든 쿼리 사슬이 실패한다.
옵셔널 체이닝은 특정 타입에서도 작동하고, 호출이 성공한지, 실패한지 확인할 수 있다.
옵셔널이 값을 가지고 있다면 프로퍼티, 메소드, 서브스크립트를 호출할 옵셔널 값 다음에 ?
를 붙여서 옵셔널 체이닝을 표시할 수 있다. 값을 강제 언래핑하는 !
키워드를 사용하는 방법과 비슷하다. 하지만 옵셔널 체이닝은 옵셔널 값이 널 값이라면 런타임 에러를 일으키지 않고 실패하지만, 강제 언래핑은 강제 언래핑을 일으킨다.
옵셔널 체이닝의 리턴 값은 옵셔널 값이다. 즉 값이 있거나 없다. 이를 통해 체이닝이 성공했는지 확인할 수 있다.
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = Person()
let roomCount = john.residence!.numberOfRooms
// this triggers a runtime error
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
// Prints "Unable to retrieve the number of rooms."
john.residence = Residence()
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
// Prints "John's residence has 1 room(s)."
Person
인스턴스 john
의 옵셔널 값을 강제 언래핑할 때에는 런타임 에러가 일어날 수도 있다. 옵셔널 바인딩, 옵셔널 체이닝을 통해 안전하게 사용하자. 간단히 말하면, if
문에 ?
체이닝을 사용하는 것이다. 값이 있다면 옵셔널 체이닝은 성공하지만, 그렇지 않으면 실패한다.
residence
에 인스턴스를 할당할 수도 있다. 인스턴스 값 또한 널 값일 수도 있기 때문에 옵셔널 바인딩으로 사용하자.
옵셔널 체이닝을 모델의 서브프로퍼티를 호출하기 위해 사용할 수 있다.
class Person {
var residence: Residence?
}
class Residence {
var rooms: [Room] = []
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
get {
return rooms[i]
}
set {
rooms[i] = newValue
}
}
func printNumberOfRooms() {
print("The number of rooms is \(numberOfRooms)")
}
var address: Address?
}
class Room {
let name: String
init(name: String) { self.name = name }
}
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if let buildingNumber = buildingNumber, let street = street {
return "\(buildingNumber) \(street)"
} else if buildingName != nil {
return buildingName
} else {
return nil
}
}
}
Residence
인스턴스를 선언하면 Room
클래스와 Address
클래스 (내부 선언) 프로퍼티에 접근하기 위해 옵셔널 체이닝을 사용한다. 구조를 살펴보면 다음과 같다.
Residence
클래스는 Room
인스턴스 배열을 프로퍼티로 가지고 있다. numberOfRooms
프로퍼티는 Room
인스턴스 배열 개수를 리턴한다. 서브스크립트를 통해 개수를 읽거나 쓸 수 있고, printNumberOfRooms
를 통해 출력도 할 수 있다. address
프로퍼티는 널 값일 수도 있도록 옵셔널로 지정되었다. address
프로퍼티 또한 내부에서 buidlingIdentifier()
등 메소드를 지원하고 있다.
Residence
인스턴스를 호출했을 때 address
프로퍼티에 접근하기 위해서는 옵셔널 체이닝을 통해 꺼내올 수 있다.
옵셔널 체이닝을 옵셔널 값을 가진 프로퍼티에 접근하거나 프로퍼티 접근이 성공했는지 확인하기 위해 사용할 수 있다.
let john = Person()
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
// Prints "Unable to retrieve the number of rooms."
let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
john.residence?.address = someAddress
func createAddress() -> Address {
print("Function was called.")
let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
return someAddress
}
john.residence?.address = createAddress()
john
인스턴스의 residence
값이 옵셔널이기 때문에 옵셔널 바인딩을 통해 널 값인지 체크한다. 널 값이기 때문에 옵셔널 체이닝이 실패할 수 있고, 프로퍼티 값을 설정할 수도 있다.