Swift オプションのチェーニング


オプションのチェーンは、プロパティ、メソッド、およびサブスクリプトをリクエストおよび呼び出すことができるプロセスです。リクエストまたは呼び出しに使用されるターゲットは nil である可能性があります。

オプションのチェーンは 2 つの値を返します:

  • ターゲットに値がある場合、呼び出しは成功し、その値を返します

  • ターゲットが nil の場合、呼び出しは nil を返します

複数のリクエストまたは呼び出しは OK です。 チェーンにリンクされています。いずれかのノードが nil の場合、チェーン全体が失敗します。


オプションのチェーンは強制解析を置き換えることができます

プロパティ、メソッド、または添え字スクリプトのオプションの値の後に疑問符 (?) を置くことで、オプションのチェーンを定義できます。

?それをオプションの値に入れてから、メソッド、属性、添字スクリプトを呼び出して値を強制展開しますオプションが 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?を返すことになります。


オプションのチェーンのモデルクラスを定義する

オプションのチェーンを使用して、複数のレベルでプロパティ、メソッド、および添字スクリプトを呼び出すことができます。これにより、それらの間で複雑なモデルを活用して下位レベルのプロパティを取得し、そのような低レベルのプロパティを正常に取得できるかどうかを確認できます。

インスタンス

は、オプション チェーンの複数の層を含む 4 つのモデル クラスを定義します。

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 の直後、添字スクリプト括弧の前に続きます。 .print はオプションです チェーンが取得しようとしているオプションの値。

例 2

この例では、john.residence に Residence インスタンスを作成し、彼の rooms 配列に 1 つ以上の Room インスタンスが存在します。その後、オプションのチェーンを使用して、Residence を通じて rooms 配列の部屋を取得できます。サブスクリプト スクリプトの例:

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("无法检索到房间")
}

上記のプログラムの実行の出力結果は次のとおりです:

第一个房间名为客厅

オプションのリンク呼び出しを通じてサブスクリプトにアクセスします

オプションのリンク呼び出しを使用すると、サブスクリプトを使用して読み取りまたは書き込みを行うことができますオプションの値を入力し、添字呼び出しが成功したかどうかを判断します。

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("无法检索到房间")
}

上記のプログラムの実行の出力結果は次のとおりです:

第一个房间名为客厅

オプションの型の添え字にアクセスする

添え字がNULL許容型の値を返す場合(SwiftのDictionaryのキー添え字など)。添字の閉じ括弧の後に疑問符を置くことで、添字の 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]

上記の例では、2 つのキーと値のペアを含む testScores 配列を定義し、String 型のキーを整数配列にマップします。

この例では、オプションの連鎖呼び出しを使用して、「Dave」配列の最初の要素を 91 に設定し、「Bev」配列の最初の要素に +1 を設定してから、「Brian」配列の最初の要素を に設定しようとします。は72です。

最初の 2 つの呼び出しは、これら 2 つのキーが存在するため成功します。しかし、キー「Brian」は辞書に存在しないため、3 回目の呼び出しは失敗します。


マルチレイヤーリンクを接続する

オプションのチェーンの複数のレイヤーを一緒に接続し、モデルの下位レベルの属性メソッドと添字スクリプトをマイニングすることができます。ただし、マルチレベルのオプション チェーンでは、すでに返されているオプションの値を超えるレベルを追加することはできません。

オプションの連鎖を通じて Int 値を取得しようとすると、使用される連鎖のレベルに関係なく、結果は常に Int? になります。 同様に、オプションのチェーンを通じて Int? 値を取得しようとすると、使用されるチェーンのレベルに関係なく、結果は常に Int? になります。

例 1

次の例では、john の居住地属性の住所の番地属性を取得しようとします。ここでは、住居属性と住所属性を接続するために 2 つのレベルのオプション チェーンが使用されており、どちらもオプションのタイプです:

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

Address As のインスタンスを設定した場合john.residence.address の値、および address の番地属性の実際の値を設定すると、複数レベルのオプションの連鎖を通じてこの属性値を取得できます。

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 許容値を返すメソッドを呼び出すこともでき、オプションの値をリンクし続けることができます。

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("未指定房间号")
}

上記プログラムの実行の出力結果は次のとおりです:

未指定房间号