Chaînage facultatif Swift

Le chaînage optionnel est un processus permettant d'interroger et d'appeler des propriétés, des méthodes et des indices. La cible de la requête ou de l'appel peut être nil.

Le chaînage optionnel renvoie deux valeurs :

  • Si la cible a une valeur, l'appel réussit et renvoie cette valeur

  • Si la cible est nil, l'appel renvoie nil

Plusieurs requêtes ou appels peuvent être enchaînés en une chaîne ; si un maillon est nil, toute la chaîne échoue.


Le chaînage optionnel peut remplacer le déballage forcé

En plaçant un point d'interrogation (?) après la valeur optionnelle d'une propriété, d'une méthode ou d'un indice, vous pouvez définir une chaîne optionnelle.

Chaînage optionnel '?' Le point d'exclamation (!) force le déballage des chaînes optionnelles de méthodes, de propriétés et d'indices
? est placé après une valeur optionnelle pour appeler des méthodes, des propriétés et des indices ! est placé après une valeur optionnelle pour appeler des méthodes, des propriétés et des indices afin de forcer le déballage de la valeur
Lorsque l’optionnel est nil, affichez un message d’erreur plus convivial Lorsque l’optionnel est nil, le déballage forcé déclenche une erreur d’exécution

Exemple de chaînage optionnel avec le point d’exclamation (!)

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}

let john = Person()

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

La sortie du programme ci-dessus est :

fatal error: unexpectedly found nil while unwrapping an Optional value

Essayer d’utiliser le point d’exclamation (!) pour forcer le déballage et obtenir la valeur de la propriété numberOfRooms de la propriété residence de cette personne provoquera une erreur d’exécution, car aucune valeur residence n’est disponible pour le déballage pour le moment.

Exemple de chaînage optionnel avec le point d’interrogation (?)

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("不能查看房间号")
}

La sortie du programme ci-dessus est :

Cannot view room number

Comme cette tentative d’obtention de numberOfRooms peut échouer, le chaînage optionnel renvoie une valeur de type Int?, également appelée « Int optionnel ». Lorsque residence est vide (comme dans l’exemple ci-dessus), l’Int optionnel vaudra nil, donc numberOfRooms ne sera pas accessible.

Il convient de noter que cela vaut également lorsque numberOfRooms est un Int non optionnel (Int?). Tant que la requête est effectuée via le chaînage optionnel, cela signifie que numberOfRooms renvoie toujours un Int? au lieu d’un Int.


Définition des classes de modèle pour le chaînage optionnel

Vous pouvez utiliser le chaînage optionnel pour appeler des propriétés, des méthodes et des indices sur plusieurs niveaux. Cela vous permet de tirer parti des modèles complexes qui les relient pour accéder à des propriétés de niveau inférieur et vérifier si ces propriétés de niveau inférieur sont accessibles avec succès.

Exemple

Quatre classes de modèle sont définies, y compris le chaînage optionnel à plusieurs niveaux :

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
        }
    }
}

Appel de méthodes via le chaînage optionnel

Vous pouvez utiliser le chaînage optionnel pour appeler une méthode sur une valeur optionnelle et vérifier si l’appel de méthode réussit. Même si la méthode ne renvoie pas de valeur, vous pouvez toujours utiliser le chaînage optionnel pour y parvenir.

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("无法输出房间号")
}

La sortie du programme ci-dessus est :

Unable to output room number

Utilisez une instruction if pour vérifier si la méthode printNumberOfRooms peut être appelée avec succès : si l’appel de méthode via le chaînage optionnel réussit, la valeur de retour implicite de printNumberOfRooms sera Void ; s’il ne réussit pas, elle renverra nil.


Utilisation du chaînage optionnel pour appeler des indices

Vous pouvez utiliser le chaînage optionnel pour essayer d’obtenir une valeur à partir d’un indice et vérifier si l’appel d’indice réussit ; toutefois, vous ne pouvez pas définir d’indice via le chaînage optionnel.

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

La sortie du programme ci-dessus est :

Unable to retrieve the room

Dans l’appel d’indice, le point d’interrogation du chaînage optionnel est placé directement après john.residence et avant les crochets d’indice, car john.residence est la valeur optionnelle que le chaînage optionnel tente d’obtenir.

Exemple 2

Dans l’exemple, créez une instance de Residence pour john.residence, et il y a une ou plusieurs instances de Room dans son tableau rooms. Vous pouvez ensuite utiliser le chaînage optionnel pour obtenir les instances du tableau rooms via l’indice de 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

let johnsAddress = Address()
johnsAddress.buildingName = "The Larches"
johnsAddress.street = "Laurel Street"
john.residence!.address = johnsAddress

if let johnsStreet = john.residence?.address?.street {
    print("John 所在的街道是 \(johnsStreet)。")
} else {
    print("无法检索到地址。 ")
}

La sortie du programme ci-dessus est :

John 所在的街道是 Laurel Street.

Accéder aux indices via des appels de chaînage optionnel

Grâce aux appels de chaînage optionnel, nous pouvons utiliser des indices pour lire ou écrire une valeur optionnelle, et déterminer si l’appel d’indice réussit.

Exemple

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

La sortie du programme ci-dessus est :

The first room is named living room

Accéder aux indices d’un type optionnel

Si un indice renvoie une valeur d’un type optionnel, comme l’indice de clé de Dictionary en Swift, vous pouvez placer un point d’interrogation après le crochet fermant de l’indice pour chaîner vers la valeur de retour optionnelle de l’indice :

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]

Dans l’exemple ci-dessus, un tableau testScores est défini, contenant deux paires clé-valeur, associant des clés de type String à un tableau d’entiers.

Cet exemple utilise des appels de chaînage optionnel pour définir le premier élément du tableau "Dave" à 91, incrémenter le premier élément du tableau "Bev" de 1, puis essayer de définir le premier élément du tableau "Brian" à 72.

Les deux premiers appels réussissent car ces deux clés existent. Mais la clé "Brian" n’existe pas dans le dictionnaire, donc le troisième appel échoue.


Connexion de plusieurs niveaux de chaînage

Vous pouvez connecter plusieurs niveaux de chaînage optionnel ensemble pour explorer plus profondément les propriétés, méthodes et indices d’un modèle. Toutefois, le chaînage optionnel multi-niveau ne peut pas ajouter plus de niveaux que la valeur optionnelle déjà renvoyée.

Si vous essayez d’obtenir une valeur Int via le chaînage optionnel, quel que soit le nombre de niveaux de chaînage que vous utilisez, le retour est toujours Int?. De même, si vous essayez d’obtenir une valeur Int? via le chaînage optionnel, quel que soit le nombre de niveaux de chaînage que vous utilisez, le retour est toujours Int?.

Exemple 1

L’exemple suivant tente d’obtenir la propriété street de l’address dans la propriété residence de john. Ici, deux niveaux de chaînage optionnel sont utilisés pour lier les propriétés residence et address, qui sont toutes deux de type optionnel :

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("不能检索地址")
}

La sortie du programme ci-dessus est :

Unable to retrieve address

Exemple 2

Si vous définissez une instance de Address comme valeur de john.residence.address, et définissez une valeur réelle pour la propriété street de address, vous pouvez obtenir cette valeur de propriété via un chaînage optionnel multi-niveau.

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

La sortie de l'exemple ci-dessus est :

The first room is the living room

Chaînage de fonctions qui renvoient des valeurs optionnelles

Nous pouvons également utiliser le chaînage optionnel pour appeler des méthodes qui renvoient des valeurs optionnelles, et continuer à chaîner sur la valeur optionnelle.

Exemple

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

La sortie du programme ci-dessus est :

No room number specified
Autres extensions