9.39. Swift optional chain

发布时间 : 2025-10-25 13:33:50 UTC      

Page Views: 10 views

An optional chain is a process that can request and invoke properties, methods, and subscripts, and the target for a request or call may be nil .

The optional chain returns two values:

  • If the target has a value, the call succeeds and returns the value

  • If the target is nil the call will return nil

Multiple requests or calls can be linked into a chain if any node is nil will cause the whole chain to fail.

9.39.1. Optional chain can replace forced parsing #

An optional chain can be defined by placing a question mark (?) after the optional value of a property, method, or subscript.

Optional chain’?’

Exclamation point (!) forces the expansion of methods, attributes, and optional chains of subscript scripts

? Call methods, properties, and subscript scripts after placing optional values

! Methods, properties, and subscript scripts are called after placing the optional value to force the expansion of the value

Output more friendly error messages when optional as nil

Force expand execution error when nil is optional

9.39.2. Use the exclamation point (!) Optional chain instance #

class Person { var residence: Residence? } class Residence { var numberOfRooms = 1 } let john = Person() //Will cause runtime errors let roomCount = john.residence!.numberOfRooms 

The output of the above program execution is as follows:

fatal error: unexpectedly found nil while unwrapping an Optional value 

Want to use the exclamation point (!) to force parsing to get this person residence attribute numberOfRooms property value, a run-time error will be raised because there is no residence value.

9.39.3. Use the question mark (?) Optional chain instance #

class Person { var residence: Residence? } class Residence { var numberOfRooms = 1 } let john = Person() // Link optional residence? Property, if residence exists, retrieve the value of numberOfRooms if let roomCount = john.residence?.numberOfRooms { print("John's room number is \(roomCount)。") } else { print("Unable to view room number") } 

The output of the above program execution is as follows:

Unable to view room number 

Because of this attempt to get numberOfRooms may fail, and the optionalchain will return Int? type value, or “optional Int”. When residence when empty (example above), select Int will be empty, so it will be inaccessible numberOfRooms the situation.

It is important to note that even if numberOfRooms yes or no option Int(Int?) . This is also true. As long as the request through the optionalchain means that in the end numberOfRooms always return a Int? instead of Int .

9.39.4. Define a model class for an optional chain #

You can use optional chains to call properties, methods, and subscript scripts at multiple levels. This allows you to take advantage of the complexmodel between them to obtain lower-level properties and check whether such underlying properties can be successfully obtained.

9.39.5. Example #

Four model classes are defined, including multi-layer optional chains:

class Person { var residence: Residence? } // Defined a variable rooms, which is initialized as an empty array of type Room [] class Residence { var rooms = [Room]() var numberOfRooms: Int { return rooms.count } subscript(i: Int) -> Room { return rooms[i] } func printNumberOfRooms() { print("Room number is \(numberOfRooms)") } var address: Address? } // Room Define a name attribute and an initializer for setting the room name class Room { let name: String init(name: String) { self.name = name } } // The final class in the model is called 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 } } } 

9.39.6. Call a method through an optional chain #

You can use the optional chain to call the method of the optional value and check whether the method call is successful. Even if this method does not return a value, you can still use an optional chain to achieve this.

class Person { var residence: Residence? } // Defined a variable rooms, which is initialized as an empty array of type Room [] class Residence { var rooms = [Room]() var numberOfRooms: Int { return rooms.count } subscript(i: Int) -> Room { return rooms[i] } func printNumberOfRooms() { print("Room number is \(numberOfRooms)") } var address: Address? } // Room defines a name attribute and an initializer that sets the room name class Room { let name: String init(name: String) { self.name = name } } // The final class in the model is called 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("Output room number") } else { print("Unable to output room number") } 

The output of the above program execution is as follows:

Unable to output room number 

Use the if statement to check whether the call can be successfully called printNumberOfRooms method: if the method is successfully called through the optional chain printNumberOfRooms the implicit return value of willbe Void if it is not successful, it returns nil .

9.39.7. Invoke the subscript script using the optional chain #

You can use the optional chain to try to get a value from the subscript script and check whether the call to the subscript script is successful. However, you cannot set the subscript script through the optional chain.

9.39.8. Example 1 #

class Person { var residence: Residence? } // Defined a variable rooms, which is initialized as an empty array of type Room [] class Residence { var rooms = [Room]() var numberOfRooms: Int { return rooms.count } subscript(i: Int) -> Room { return rooms[i] } func printNumberOfRooms() { print("Room number is\(numberOfRooms)") } var address: Address? } // Room defines a name attribute and an initializer that sets the room name class Room { let name: String init(name: String) { self.name = name } } // The final class in the model is called 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("First room name \(firstRoomName).") } else { print("Unable to retrieve room") } 

The output of the above program execution is as follows:

Unable to retrieve room 

The question mark of the optional chain in the subscript call comes directlyafter the john.residence and before the subscript parentheses, because john.residence is the optional value that the optional chain is trying to get.

9.39.9. Example 2 #

Instance to create a Residence give an example to john.residence and in his rooms . There are one or more objects in the array Room instance, then you can use the optional chain to pass the Residence thesubscript script is obtained in the rooms the instance in the array:

class Person { var residence: Residence? } // Defined a variable rooms, which is initialized as an empty array of type Room [] class Residence { var rooms = [Room]() var numberOfRooms: Int { return rooms.count } subscript(i: Int) -> Room { return rooms[i] } func printNumberOfRooms() { print("Room number is \(numberOfRooms)") } var address: Address? } // Room defines a name attribute and an initializer that sets the room name class Room { let name: String init(name: String) { self.name = name } } // The final class in the model is called 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: "living room")) johnsHouse.rooms.append(Room(name: "kitchen")) 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's street is \(johnsStreet)。") } else { print("Unable to retrieve address. ") } 

The output of the above program execution is as follows:

John's street is Laurel Street。 

9.39.10. Access the subscript through an optional link call #

Through the optional link call, we can use the subscript to read or write the optional value and determine whether the subscript call is successful ornot.

9.39.11. Example #

class Person { var residence: Residence? } // Defined a variable rooms, which is initialized as an empty array of type Room [] class Residence { var rooms = [Room]() var numberOfRooms: Int { return rooms.count } subscript(i: Int) -> Room { return rooms[i] } func printNumberOfRooms() { print("Room number is \(numberOfRooms)") } var address: Address? } // Room defines a name attribute and an initializer that sets the room name class Room { let name: String init(name: String) { self.name = name } } // The final class in the model is called 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: "living room")) johnsHouse.rooms.append(Room(name: "kitchen")) john.residence = johnsHouse if let firstRoomName = john.residence?[0].name { print("The first room is named\(firstRoomName)") } else { print("Unable to retrieve room") } 

The output of the above program execution is as follows:

The first room is called the living room 

9.39.12. Access the subscript of the optional type #

If the subscript returns a nullable type value, such as in Swift Dictionary of key subscript. You can put a question mark after the closing parentheses of the subscript to link the nullable return value of the subscript:

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] 

In the above example, a testScores array, which contains two key-value pairs, set the String of type key maps to an array of integers.

This example uses an optional link call to set the first element in the “Dave” array to 91, the first element in the “Bev” array + 1, and then tries to set the first element in the “Brian” array to 72.

The first two calls are successful because these two key exist. But the key “Brian” does not exist in the dictionary, so the third call fails.

9.39.13. Connect multi-layer links #

You can connect multiple layers of optional chains together, and you can digup property methods and subscript scripts at a lower level in the model. However, the multi-layer optional chain cannot add more layers than the optional values that have been returned.

If you try to get it through the optional chain Int value, no matter how many layers of links are used, the Int? . Similarly, if you try to get through the optional chain Int? value, no matter how many layers oflinks are used, the Int? .

9.39.14. Example 1 #

The following example attempts to get the john of residence in theattribute address of street property. Two layers of optional chains are used to contact. residence and address property, both of which are optional types

class Person { var residence: Residence? } // Defined a variable rooms, which is initialized as an empty array of type Room [] class Residence { var rooms = [Room]() var numberOfRooms: Int { return rooms.count } subscript(i: Int) -> Room { return rooms[i] } func printNumberOfRooms() { print("Room number is \(numberOfRooms)") } var address: Address? } // Room defines a name attribute and an initializer that sets the room name class Room { let name: String init(name: String) { self.name = name } } // The final class in the model is called 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's address is \(johnsStreet).") } else { print("Unable to retrieve address") } 

The output of the above program execution is as follows:

Unable to retrieve address 

9.39.15. Example 2 #

If you work for Address set an instance to act as john.residence.address and the value of address of street property sets an actual value, which you can get through a multi-layer optional chain.

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("Room number 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 (buildingName != nil) { return buildingName } else if (buildingNumber != nil) { return buildingNumber } else { return nil } } } let john = Person() 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 is \(firstRoomName)") } else { print("Unable to retrieve room") } 

The output result of the above example is:

The first room is the living room 

9.39.16. Link functions that return optional values #

We can also call methods that return nullable values through optional links,and we can continue to link optional values.

9.39.17. Example #

class Person { var residence: Residence? } // Defined a variable rooms, which is initialized as an empty array of type Room [] class Residence { var rooms = [Room]() var numberOfRooms: Int { return rooms.count } subscript(i: Int) -> Room { return rooms[i] } func printNumberOfRooms() { print("Room number is \(numberOfRooms)") } var address: Address? } // Room defines a name attribute and an initializer that sets the room name class Room { let name: String init(name: String) { self.name = name } } // The final class in the model is called 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("Room number specified)") } else { print("No room specified room number") } 

The output of the above program execution is as follows:

No room specified room number 
《地理信息系统原理、技术与方法》  97

最近几年来,地理信息系统无论是在理论上还是应用上都处在一个飞速发展的阶段。 GIS被应用于多个领域的建模和决策支持,如城市管理、区划、环境整治等等,地理信息成为信息时代重要的组成部分之一; “数字地球”概念的提出,更进一步推动了作为其技术支撑的GIS的发展。 与此同时,一些学者致力于相关的理论研究,如空间感知、空间数据误差、空间关系的形式化等等。 这恰好说明了地理信息系统作为应用技术和学科的两个方面,并且这两个方面构成了相互促进的发展过程。