티스토리 뷰

출처

http://kka7.tistory.com/22





옵셔널 체이닝

옵셔널이 현재 nil 이 될 수 있는 프로퍼티, 메소드, 서브 스크립트를
조회하고 호출하는 과정이다.

옵셔널 체이닝에 값이 있으면 프로퍼티, 메소드, 스크립트 호출에 성공한다.
옵셔널이 nil 이면 프로퍼티, 메소드, 스크립트 호출은 nil 을 반환한다.

여러 개를 함께 연결할 수 있고 연결된 어떤 링크가 nil 이면 전체 체인은 실패한다.



강제 언래핑 대신 옵셔널 체이닝

nil 값을 가진 옵셔널에 강제 언래핑을 사용하면 runtime 오류가 발생한다.

옵셔널 체이닝 호출의 결과는 항상 옵셔널 값이며,
프로퍼티, 메소드, 서브스크립트 조회하여 옵셔널이 아닌 값을 반환하더라도 마찬가지이다.

class Person {

    var residence: Residence?

}


class Residence {

    var numberOfRooms = 1

}


let john = Person()


let roomCount = john.residence!.numberOfRooms

// this triggers a runtime error


위 코드는 residence 가 nil 일 때 강제 언래핑을 하려 시도하기 때문에

runtime error 가 발생한다.


옵셔널 체이닝은 numberOfRooms 에 접근하는 방법을 제공한다.

옵셔널 체이닝을 사용하기 위해선 ( ! ) 대신 ( ? ) 을 사용한다.


if let roomCount = john.residence?.numberOfRooms {

    print("John's residence has \(roomCount) room(s).")

} else {

    print("Unable to retrieve the number of rooms.")

}


residence 옵셔널 프로퍼티에서 "체인" 은 residence 가 존재하면

numberOfRooms 의 값을 가져오는 것을 말한다.


numberOfRooms 에 접근을 시도하는 것은 실패할 가능성이 있기 때문에

옵셔널 체이닝은 Int? 타입의 값이나 옵셔널 Int 값을 반환하려고 한다.


위 예제처럼 residence 가 nil 일 때 옵셔널 Int 는 nil 이 될 것이며

numberOfRooms 에 접근하는 것이 가능하지 않다는 사실을 알려준다.


옵셔널 Int 는 정수를 언래핑하기 위해 옵셔널 바인딩으로 접근되고

roomCount 변수에 옵셔널이 아닌 값을 할당한다.


numberOfRooms 는 옵셔널 값이 아닌 Int 로 정의되어 있지만

옵셔널 체인을 통해 조회되는 numberOfRooms 는 Int? 를 반환한다.






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

하위 프로퍼티에서 프로퍼티, 메소드, 서브스크립트에 접근이 가능한지 확인할 수 있다.

class Person {

    var residence: Residence?

}


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

        }

    }

}


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?

}




옵셔널 체이닝을 이용하여 프로퍼티에 접근하기

옵셔널 체이닝을 이용해서 프로퍼티의 값을 설정하려는 것을 시도할 수 있다.


하지만, 옵셔널 값이 nil 이라면 시도조차 하지 않는다.


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()


createAddress() 함수를 호출해도 "Function was called" 라는 메소드는

호출되지 않는다.




옵셔널 체이닝을 이용하여 메소드 호출

옵셔널 체이닝으로 호출될 때 반환 값은 언제나 옵셔널 타입이기 때문에
반환값이 Void 인 함수를 호출했다면 Void? 값이 된다.

이는 if 문을 이용하여 printNumberOfRooms() 메소드를 호출할 수 있는지 확인할 수 있는
기회를 제공한다.

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


john.residence?[0] = Room(name: "Bathroom")



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     //[91, 82, 84]

testScores["Bev"]?[0] += 1      //[80, 94, 81]

testScores["Brian"]?[0] = 72    //nil






체이닝의 여러 단계 연결하기

값을 반환하기 위해 옵셔널 체이닝의 여러 단계에 더 이상 옵셔널 단계를 추가하지 않는다.

즉, 옵셔널 체이닝을 이용해서 Int 값을 가져오려고 시도하면
얼마나 많은 체이닝이 사용되었든 언제나 Int? 값이 반환된다.

동일하게, Int? 값을 가져오려고 시도해도 언제나 Int? 값이 반환된다.

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




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

옵셔널 타입의 값을 반환하는 메소드를 호출하기 위해
옵셔널 체이닝을 사용할 수 있고 필요에 따라
메소드의 반환값에 연결한다.

예제는 Address 클래스의 buildingIdentifier() 메소드 (반환값 String? )를 호출한다.

얼마나 중첩되었던 옵셔널 체이닝 이후 호출된 메소드의 return type 은 String? 이다.

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







공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함