본문 바로가기
⌨️ Language/swift

[Swift] 옵셔널 체이닝

by hyebin (Helia) 2024. 3. 26.

옵셔널 체이닝

nil일 수도 있는 프로퍼티나, 메소드 그리고 서브스크립트에 질의(query)를 하는 과정

  • 만약 옵셔널이 프로퍼티나 메소드 혹은 서브스크립트에 대한 값을 갖고 있다면 그 값을 반환함
  • 만약 값이 nil이라면 nil 반환

 

강제 언래핑의 대체로써의 옵셔널 체이닝

  • 옵셔널 값 뒤에 물음표(?)를 붙여서 표현 가능
  • 옵셔널을 사용할 수 있는 값에는 프로퍼티, 메소드 그리고 서비스크립트가 포함
  • 옵셔널 값을 강제 언래핑하기 위해 뒤에 느낌표(!)를 붙이는 것과 유사
    • 강제 언래핑을 했는데 만약 그 값이 없으면 런타임 에러 발생
    • 옵셔널 체이닝을 사용하면 런타임 에러 대신 nil이 반환
class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}

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."

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)."

 

옵셔널 체이닝을 위한 모델 클래스 정의

  • 한 단계가 아닌 여러 단계(multi-level optional chaining) 사용할 수 있음
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
        }
    }
}

 

옵셔널 체이닝을 통한 프로퍼티의 접근

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
  • john.residence?가 nil이기 때문에 address 할당은 실패함
  • 할당도 할당받는 왼쪽 항이 nil이면 아예 오른쪽 항이 실행되지 않음
func createAddress() -> Address {
    print("Function was called.")
  let someAddress = Address()
    someAddress.buildingNumber = "29"
    someAddress.street = "Acacia Road"    

    return someAddress
}
john.residence?.address = createAddress()

 

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

func printNumberOfRooms() {
    print("The number of rooms is \\(numberOfRooms)")
}

if john.residence?.printNumberOfRooms() != nil {
    print("It was possible to print the number of rooms.")
} else {
    print("It was not possible to print the number of rooms.")
}
// Prints "It was not possible to print the number of rooms."

if (john.residence?.address = someAddress) != nil {
    print("It was possible to set the address.")
} else {
    print("It was not possible to set the address.")
}
// Prints "It was not possible to set the address."

 

옵셔널 체이닝을 통한 서브스크립트 접근

if let firstRoomName = john.residence?[0].name {
    print("The first room name is \\(firstRoomName).")
} else {
    print("Unable to retrieve the first room name.")
}
// Prints "Unable to retrieve the first room name."

let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "Living Room"))
johnsHouse.rooms.append(Room(name: "Kitchen"))
john.residence = johnsHouse

 if let firstRoomName = john.residence?[0].name {
    print("The first room name is \\(firstRoomName).")
} else {
    print("Unable to retrieve the first room name.")
}
// Prints "The first room name is Living Room."
  • 옵셔널 타입의 서브스크립트 접근
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0] += 1
testScores["Brian"]?[0] = 72
// the "Dave" array is now [91, 82, 84] and the "Bev" array is now [80, 94, 81]

 

 

체이닝의 다중 레벨 연결

  • 옵셔널 체이닝의 상위 레벨 값이 옵셔널인 경우 현재 값이 옵셔널이 아니더라도 그 값은 옵셔널값이 됩니다.
  • 옵셔널 체이닝의 상위 레벨 값이 옵셔널이고 현재 값이 옵셔널 이라고 해서 더 옵셔널하게 되진 않습니다.
  • 옵셔널 체이닝을 통해 값을 검색하거나 메소드를 호출하면 몇 단계를 거치는지 상관없이 옵셔널을 반환합니다.
if let johnsStreet = john.residence?.address?.street {
    print("John's street name is \\(johnsStreet).")
} else {
    print("Unable to retrieve the address.")
}
// Prints "Unable to retrieve the address."
let johnsAddress = Address()
johnsAddress.buildingName = "The Larches"
johnsAddress.street = "Laurel Street"
john.residence?.address = johnsAddress

if let johnsStreet = john.residence?.address?.street {
    print("John's street name is \\(johnsStreet).")
} else {
    print("Unable to retrieve the address.")
}
// Prints "John's street name is Laurel Street."

 

체이닝에서 옵셔널 값을 반환하는 메소드

if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
    print("John's building identifier is \\(buildingIdentifier).")
}
// Prints "John's building identifier is The Larches."

if let beginsWithThe =
    john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
    if beginsWithThe {
        print("John's building identifier begins with \\"The\\".")
    } else {
        print("John's building identifier does not begin with \\"The\\".")
		}
}
// Prints "John's building identifier begins with "The"."
반응형

'⌨️ Language > swift' 카테고리의 다른 글

[Swift] 프로토콜  (0) 2024.03.29
[Swift] 에러 처리  (0) 2024.03.28
[Swift] 초기화  (1) 2024.03.25
[Swift] 상속  (0) 2024.03.22
[Swift] 서브스크립트  (0) 2024.03.22

댓글