신속한 선택적 체이닝


Optional Chaining은 속성, 메서드 및 첨자를 요청하고 호출할 수 있는 프로세스입니다. 요청 또는 호출에 사용되는 대상은 nil일 수 있습니다.

선택적 체인은 두 가지 값을 반환합니다.

  • 대상에 값이 있으면 호출이 성공하고 해당 값을 반환합니다.

  • 대상이 nil이면 호출은 nil을 반환합니다.

여러 요청 또는 호출은 정상입니다. 체인에 연결되어 있습니다. 노드가 0이면 전체 체인이 실패합니다.


옵션 체인은 강제 구문 분석을 대체할 수 있습니다.

옵션 체인은 속성, 메서드 또는 첨자 스크립트의 옵션 값 뒤에 물음표(?)를 배치하여 정의할 수 있습니다.

옵션 체인 '?'느낌표(!)는 메소드, 속성, 첨자 스크립트 옵셔널 체인을 강제로 확장합니다
? 옵셔널 값에 배치하고 나중에 메소드, 속성, 첨자 스크립트를 호출합니다 ! 옵셔널 값에 넣은 다음 메소드, 속성, 서브스크립트를 호출하여 값을 강제로 확장합니다
옵셔널이 nil이면 좀 더 친숙한 오류 메시지가 출력됩니다옵셔널이 nil이면 실행 오류는 다음과 같습니다. 강제 확장

느낌표(!) 사용 선택적 체인 예시

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}

let john = Person()

//将导致运行时错误
let roomCount = john.residence!.numberOfRooms

위 프로그램 실행 출력 결과는

fatal error: unexpectedly found nil while unwrapping an Optional value


느낌표(!)를 사용하여 강제로 구문 분석하여 이 사람의 거주 속성 중 numberOfRooms 속성 값을 가져오려는 경우 , 현재 분석 가능한 거주 가치가 없기 때문에 런타임 오류가 발생합니다.


느낌표(!) 선택적 체인 인스턴스 사용

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}

let john = Person()

// 链接可选residence?属性,如果residence存在则取回numberOfRooms的值
if let roomCount = john.residence?.numberOfRooms {
    print("John 的房间号为 \(roomCount)。")
} else {
    print("不能查看房间号")
}

위 프로그램 실행의 출력 결과는 다음과 같습니다.

不能查看房间号

numberOfRooms를 얻으려는 시도가 실패할 수 있으므로 선택적 체인은 Int? 또는 "선택적 Int"라고 불리는 것입니다. 거주지가 비어 있으면(위 예) 선택한 Int가 비어 있으므로 numberOfRooms에 액세스할 수 없습니다.

이는 numberOfRooms가 선택사항이 아닌 Int(Int?)인 경우에도 마찬가지입니다. 요청이 선택적 체인을 통과하는 한 최종 numberOfRooms는 항상 Int? 대신 Int?를 반환한다는 의미입니다.


옵션 체인에 대한 모델 클래스 정의

옵션 체인을 사용하여 여러 수준에서 속성, 메서드 및 아래 첨자 스크립트를 호출할 수 있습니다. 이를 통해 그들 사이의 복잡한 모델을 활용하여 하위 수준 속성을 얻을 수 있고, 이러한 하위 수준 속성을 성공적으로 얻을 수 있는지 확인할 수 있습니다.

인스턴스

옵션 체인의 여러 레이어를 포함하는 네 가지 모델 클래스를 정의합니다.

class Person {
    var residence: Residence?
}

// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {
    var rooms = [Room]()
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        return rooms[i]
    }
    func printNumberOfRooms() {
        print("房间号为 \(numberOfRooms)")
    }
    var address: Address?
}

// Room 定义一个name属性和一个设定room名的初始化器
class Room {
    let name: String
    init(name: String) { self.name = name }
}

// 模型中的最终类叫做Address
class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if (buildingName != nil) {
            return buildingName
        } else if (buildingNumber != nil) {
            return buildingNumber
        } else {
            return nil
        }
    }
}

옵션 체인을 통해 메서드 호출

옵션 체인을 사용하여 옵션 값에 대한 메서드를 호출하고 메서드 호출을 검사할 수 있습니다. 성공 여부 아니다. 이 메서드가 값을 반환하지 않더라도 이 목적으로 선택적 연결을 계속 사용할 수 있습니다.

class Person {
    var residence: Residence?
}

// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {
    var rooms = [Room]()
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        return rooms[i]
    }
    func printNumberOfRooms() {
        print("房间号为 \(numberOfRooms)")
    }
    var address: Address?
}

// Room 定义一个name属性和一个设定room名的初始化器
class Room {
    let name: String
    init(name: String) { self.name = name }
}

// 模型中的最终类叫做Address
class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if (buildingName != nil) {
            return buildingName
        } else if (buildingNumber != nil) {
            return buildingNumber
        } else {
            return nil
        }
    }
}

let john = Person()


if ((john.residence?.printNumberOfRooms()) != nil) {
    print("输出房间号")
} else {
    print("无法输出房间号")
}

위 프로그램 실행의 출력 결과는 다음과 같습니다.

无法输出房间号

if 문을 사용하여 printNumberOfRooms 메서드가 성공적으로 호출될 수 있는지 확인합니다. 옵션 체인을 통해 메서드가 성공적으로 호출되면 printNumberOfRooms의 암시적 반환 값은 다음과 같습니다. 성공하지 못하면 nil을 반환합니다.


옵셔널 체인을 사용하여 첨자 스크립트 호출

옵셔널 체인을 사용하여 첨자 스크립트에서 값을 가져오고 첨자 스크립트 호출이 성공했는지 확인할 수 있지만 옵셔널 체이닝을 통해 첨자 스크립트를 설정할 수는 없습니다. .

예제 1

class Person {
    var residence: Residence?
}

// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {
    var rooms = [Room]()
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        return rooms[i]
    }
    func printNumberOfRooms() {
        print("房间号为 \(numberOfRooms)")
    }
    var address: Address?
}

// Room 定义一个name属性和一个设定room名的初始化器
class Room {
    let name: String
    init(name: String) { self.name = name }
}

// 模型中的最终类叫做Address
class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if (buildingName != nil) {
            return buildingName
        } else if (buildingNumber != nil) {
            return buildingNumber
        } else {
            return nil
        }
    }
}

let john = Person()
if let firstRoomName = john.residence?[0].name {
    print("第一个房间名 \(firstRoomName).")
} else {
    print("无法检索到房间")
}

위 프로그램 실행의 출력 결과는 다음과 같습니다.

无法检索到房间

아래 첨자 스크립트 호출에서 옵션 체인의 물음표는 circname.print 바로 뒤와 첨자 스크립트 대괄호 앞에 옵니다. 왜냐하면 circname이 .print는 선택적입니다. 체인이 얻으려고 하는 선택적 값입니다.

예제 2

예제에서 john.residence에 Residence 인스턴스를 생성하고 그의 방 배열에 하나 이상의 Room 인스턴스가 있는 경우 옵션 체인을 사용하여 Residence 첨자 스크립트를 통해 방 배열에서 이를 얻을 수 있습니다. 예:

class Person {
    var residence: Residence?
}

// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {
    var rooms = [Room]()
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        return rooms[i]
    }
    func printNumberOfRooms() {
        print("房间号为 \(numberOfRooms)")
    }
    var address: Address?
}

// Room 定义一个name属性和一个设定room名的初始化器
class Room {
    let name: String
    init(name: String) { self.name = name }
}

// 模型中的最终类叫做Address
class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if (buildingName != nil) {
            return buildingName
        } else if (buildingNumber != nil) {
            return buildingNumber
        } else {
            return nil
        }
    }
}

let john = Person()
let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "客厅"))
johnsHouse.rooms.append(Room(name: "厨房"))
john.residence = johnsHouse

if let firstRoomName = john.residence?[0].name {
    print("第一个房间名为\(firstRoomName)")
} else {
    print("无法检索到房间")
}

위 프로그램 실행의 출력 결과는 다음과 같습니다.

第一个房间名为客厅

선택적 링크 호출을 통해 아래 첨자에 액세스

선택적 링크 호출을 사용하면 아래 첨자를 사용하여 선택 사항을 읽거나 쓸 수 있습니다. 값을 입력하고 첨자 호출이 성공했는지 확인합니다.

Example

class Person {
    var residence: Residence?
}

// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {
    var rooms = [Room]()
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        return rooms[i]
    }
    func printNumberOfRooms() {
        print("房间号为 \(numberOfRooms)")
    }
    var address: Address?
}

// Room 定义一个name属性和一个设定room名的初始化器
class Room {
    let name: String
    init(name: String) { self.name = name }
}

// 模型中的最终类叫做Address
class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if (buildingName != nil) {
            return buildingName
        } else if (buildingNumber != nil) {
            return buildingNumber
        } else {
            return nil
        }
    }
}

let john = Person()

let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "客厅"))
johnsHouse.rooms.append(Room(name: "厨房"))
john.residence = johnsHouse

if let firstRoomName = john.residence?[0].name {
    print("第一个房间名为\(firstRoomName)")
} else {
    print("无法检索到房间")
}

위 프로그램 실행의 출력 결과는 다음과 같습니다.

第一个房间名为客厅

Accessing the subscript of an option type

subscript가 Swift의 Dictionary 키 하위 첨자와 같은 null 허용 유형 값을 반환하는 경우. 아래 첨자의 닫는 괄호 뒤에 물음표를 배치하여 아래 첨자의 null 허용 반환 값을 연결할 수 있습니다.

var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0]++
testScores["Brian"]?[0] = 72
// the "Dave" array is now [91, 82, 84] and the "Bev" array is now [80, 94, 81]

위의 예는 두 개의 키-값 쌍을 포함하고 문자열 유형 키를 정수 배열에 매핑하는 testScores 배열을 정의합니다.

이 예에서는 선택적 연결 호출을 사용하여 "Dave" 배열의 첫 번째 요소를 91로 설정하고 "Bev" 배열의 첫 번째 요소에 +1을 설정한 다음 "Brian" 배열의 첫 번째 요소를 72입니다.

이 두 키가 존재하므로 처음 두 호출은 성공합니다. 그러나 키 "Brian"이 사전에 존재하지 않으므로 세 번째 호출이 실패합니다.


다층 링크 연결

다층 옵셔널 체인을 함께 연결할 수 있고 모델의 하위 속성 메서드와 첨자 스크립트를 마이닝할 수 있습니다. 그러나 다중 레벨 옵션 체인은 이미 반환된 옵션 값보다 더 많은 레벨을 추가할 수 없습니다.

선택적 체이닝을 통해 Int 값을 얻으려고 하면 얼마나 많은 체이닝 수준을 사용하더라도 결과는 항상 Int?입니다. 마찬가지로 선택적 연결을 통해 Int? 값을 얻으려고 하면 사용된 연결 수준에 관계없이 결과는 항상 Int?가 됩니다.

예제 1

다음 예에서는 john의 거주지 속성에서 주소의 거리 속성을 얻으려고 시도합니다. 여기에는 두 개의 선택적 체인 레이어가 거주지와 주소 속성을 연결하는 데 사용되며 둘 다 선택적 유형입니다.

class Person {
    var residence: Residence?
}

// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {
    var rooms = [Room]()
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        return rooms[i]
    }
    func printNumberOfRooms() {
        print("房间号为 \(numberOfRooms)")
    }
    var address: Address?
}

// Room 定义一个name属性和一个设定room名的初始化器
class Room {
    let name: String
    init(name: String) { self.name = name }
}

// 模型中的最终类叫做Address
class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if (buildingName != nil) {
            return buildingName
        } else if (buildingNumber != nil) {
            return buildingNumber
        } else {
            return nil
        }
    }
}

let john = Person()

if let johnsStreet = john.residence?.address?.street {
    print("John 的地址为 \(johnsStreet).")
} else {
    print("不能检索地址")
}

위 프로그램 실행의 출력은 다음과 같습니다.

不能检索地址

예제 2

주소에 대한 인스턴스를 설정하는 경우 john.residence.address의 값과 address의 street 속성에 대한 실제 값을 설정하면 여러 수준의 선택적 연결을 통해 이 속성 값을 얻을 수 있습니다.

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("房间号为 \(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 (buildingName != nil) {
            return buildingName
        } else if (buildingNumber != nil) {
            return buildingNumber
        } else {
            return nil
        }
    }
}
let john = Person()
john.residence?[0] = Room(name: "浴室")

let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "客厅"))
johnsHouse.rooms.append(Room(name: "厨房"))
john.residence = johnsHouse

if let firstRoomName = john.residence?[0].name {
    print("第一个房间是\(firstRoomName)")
} else {
    print("无法检索房间")
}

위 예제의 출력 결과는 다음과 같습니다.

第一个房间是客厅

옵션 값을 반환하는 함수 연결

옵션 연결을 통해 null 허용 값을 반환하는 메서드를 호출할 수도 있고, 계속해서 옵션 값을 연결할 수도 있습니다.

Example

class Person {
    var residence: Residence?
}

// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {
    var rooms = [Room]()
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        return rooms[i]
    }
    func printNumberOfRooms() {
        print("房间号为 \(numberOfRooms)")
    }
    var address: Address?
}

// Room 定义一个name属性和一个设定room名的初始化器
class Room {
    let name: String
    init(name: String) { self.name = name }
}

// 模型中的最终类叫做Address
class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if (buildingName != nil) {
            return buildingName
        } else if (buildingNumber != nil) {
            return buildingNumber
        } else {
            return nil
        }
    }
}

let john = Person()

if john.residence?.printNumberOfRooms() != nil {
    print("指定了房间号)")
}  else {
    print("未指定房间号")
}

위 프로그램 실행 출력 결과는 다음과 같습니다.

未指定房间号