Swift Programming Language -- Optional chain (Optional Chaining), swiftoptional
Optional Chaining is a process that allows you to request and call attributes, methods, and subscripts, its self-sufficiency is reflected in that the current request or call target may be null (nil ). If the self-determined target has a value, the call will succeed. On the contrary, if the selected target is null (nil), the call will return NULL (nil ). Multiple requests or calls can be linked together to form a chain. If any node is empty (nil), the entire chain will become invalid.
Note: The self-judgment chain of Swift is similar to the message in Objective-C, but Swift can be used in any type and the failure or failure can be detected.
Optional chain can replace forced resolution
You can define an optional chain by placing a question mark after the optional value (optional value) of the property, method, or sub-script you want to call. This is similar to placing a declarative symbol behind an optional value to forcibly split the value in the package. The main difference between them is that when the optional value is null, the optional chain fails immediately. However, a general forced resolution will cause a runtime error.
To reflect whether the optional chain can call null (nil), whether or not the returned values of the properties, methods, and subscripts you call are optional values, the returned results are all optional values. You can use this return value to check whether your optional chain is successfully called. If yes, the call succeeds. If yes, the call fails if nil is returned.
The returned results of the call of the optional chain have the same type as the original returned results, but the original returned results are encapsulated into an optional value. When the optional chain call is successful, an attribute that should return Int will return Int ?.
The following sections of Code explain the differences between the optional chain and forced resolution.
First, define two classes: Person and Residence.
class Person { var residence: Residence?} class Residence { var numberOfRooms = 1}
Residence has a numberOfRooms of the Int type and its value is 1. A Person has a residence property of self-judgment. Its type is Residence ?.
If you create a new Person instance, its residence attribute is defined as a user-judgment type, which will be initialized blank by default:
let john = Person()
If you want to use an exclamation point (!) Forcing resolution to obtain the numberOfRooms property value of this residence property will cause a runtime error because there is no residence value for resolution.
let roomCount =john.residence!.numberOfRooms
// It will cause a running error
When john. residence is not nil, it runs and sets roomCount to a reasonable value of the int type. However, as mentioned above, when the residence is empty, this code will cause a runtime error.
The optional chain provides another method to obtain numberOfRooms. Use optional links and question marks to replace the original! Location:
If let roomCount = john. residence ?. NumberOfRooms {println ("John's residence has \ (roomCount) room (s ). ")} else {println (" Unable to retrieve the number of rooms. ")} // print" Unable toretrieve the number of rooms.
This tells Swift to determine residence by link? Property. If residence exists, the value of numberOfRooms is retrieved.
This operation may fail to obtain numberOfRooms. The optional chain will return Int? Type value, or "self-judgment Int ". When the residence is empty (in the previous example), selecting Int will be empty, so numberOfRooms cannot be accessed first.
Note that even if numberOfRooms is not a self-judged Int (Int ?) This is also true. As long as the request passes the optional link, it means that the final numberOfRooms always returns an Int? Instead of Int.
You can define a Residence instance for john. residence so that it is no longer blank:
john.residence = Residence()
John. residence now has an actual instance instead of nil. If you want to use the same optional chain as before to obtain numberOfRoooms, it will return an Int containing the default value of 1? :
If let roomCount = john. residence ?. NumberOfRooms {println ("John's residence has \ (roomCount) room (s ). ")} else {println (" Unable to retrieve the number of rooms. ")} // print" John's residence has 1 room (s )".
Define model classes for optional chains
You can use optional links to call attributes, methods, and subscripts in multiple layers. This allows you to use the complex models between them to obtain more underlying attributes and check whether such underlying attributes can be obtained successfully.
The code below defines four model classes to be used later, including multi-layer optional chains. These classes are extended by adding a Room and an Address class in the above Person and Residence model.
The Person class is defined as before.
class Person { var residence: Residence?}
Residence is more complex than before. This time, it defines a variable "rooms", which is initialized as an empty array of the Room [] type:
class Residence { var rooms = Room[]() var numberOfRooms: Int { return rooms.count } subscript(i: Int) -> Room { return rooms[i] } func printNumberOfRooms() { println("The number of rooms is \(numberOfRooms)") } var address: Address?}
Because Residence stores an array of Room instances, its numberOfRooms attribute value is calculated instead of a fixed storage value. The numberOfRooms attribute value is obtained by returning the count attribute value of the rooms array.
To quickly access the rooms array, Residence defines a read-only sub-script, which can be successfully called by inserting the element badge of the array. If the badge exists, the subscript returns the element.
Residence also provides a printNumberOfRooms method, that is, a simple number of printed rooms.
Finally, Residence defines a self-judgment attribute called address (address ?). The attribute of the Address class will be defined later. The Room class used for the room array is a very simple class, which has only one name attribute and an initialized device for setting the Room name.
class Room { let name: String init(name: String) { self.name = name }}
The final class in this model is called Address. It has three self-judgment attributes. The value type is String ?. The first two self-judgment attributes buildingName and buildingNumber are part of the address. They are two ways to define a building. The third property street is the street name used to name the address:
class Address { var buildingName: String? var buildingNumber: String? var street: String? func buildingIdentifier() -> String? { if buildingName { return buildingName } else if buildingNumber { return buildingNumber } else { return nil } }}
The Address class also provides a buildingIdentifier method. Its return value type is String ?. This method checks the attributes of buildingName and buildingNumber. If buildingName has a value, it will be returned, or if buildingNumber has a value, it will be returned, or if no property has a value, it will return null.
Call attributes through optional links
As described in "Alternative force resolution of optional links" above, you can use the optional values of optional links to obtain attributes and check whether the attributes are obtained successfully. However, you cannot assign values to attributes using optional links.
Use the class defined above to create a person instance, and try again to go to its numberOfRooms attribute:
Let john = Person () if let roomCount = john. residence ?. NumberOfRooms {println ("John's residence has \ (roomCount) room (s ). ")} else {println (" Unable to retrieve the number of rooms. ")} // print" Unable toretrieve the number of rooms.
Because john. residence is empty, this optional link fails as before, but there is no runtime error.
Call methods using optional links
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 the optional chain to achieve this goal.
The printNumberOfRooms method of Residence prints the current value of numberOfRooms. The method is as follows:
func printNumberOfRooms(){ println(“The number of rooms is \(numberOfRooms)”)}
This method does not return values. However, functions and methods Without Return value types have an implicit Return value type Void (see Function Without Return Values ).
If you use the optional chain to call this method, the return value type of this method will be Void ?, Instead of Void, because the return value is always optional type (optional type) when the method is called through the optional chain )., Even if this method does not define the return value, you can use the if statement to check whether the printNumberOfRooms method can be successfully called: if the method is successfully called through the optional chain, the implicit return value of printNumberOfRooms will be Void, if not, nil is returned:
If john. residence ?. PrintNumberOfRooms () {println ("It was possible to print the number of rooms. ")} else {println (" It was not possible to print the number of rooms. ")} // print" It was notpossible to print the number of rooms. ".
Use the optional link to call the subscript
You can use the optional link to obtain the value from the sub-script and check whether the Sub-script call is successful. However, you cannot use the optional link to set the sub-code.
Note: When you use the optional link to obtain the sub-script, you should put the question mark in front of the sub-script brackets instead of the following. The question mark of the optional link is generally followed by the self-judgment Expression statement.
The following example uses the child script defined in the Residence class to obtain the name of the first room in the john. residence array. Because john. residence is now nil, the sub-script call fails.
If let firstRoomName = john. residence? [0]. name {println ("The first room name is \ (firstRoomName ). ")} else {println (" Unable to retrieve the first room name. ")} // print" Unable toretrieve the first room name. ".
In a sub-code call, the question mark of the optional link directly follows john. residence, before the sub-script brackets, because john. residence is the optional value that the optional link tries to obtain.
If you create a Residence instance for john. residence, and there is one or more Room instances in his Room array, you can use the optional chain to get the instances in the Room array through the Residence sub-script:
Let johnsHouse = Residence () johnsHouse. rooms + = Room (name: "LivingRoom") johnsHouse. rooms + = Room (name: "Kitchen") john. residence = johnsHouse if let firstRoomName = john. residence? [0]. name {println ("Thefirst room name is \ (firstRoomName ). ")} else {println (" Unable to retrieve the first room name. ")} // print" The firstroom name is Living Room. ".
Connect multiple links
You can connect multiple layers of optional links to obtain the attribute methods and subscripts of the lower layers in the model. However, you cannot add more layers than the available values returned by the multi-layer optional chain. That is to say:
If the type you are trying to obtain is not an optional type, it will become an optional type because the optional chain is used. If the type you are trying to obtain is already an optional type, it will not improve self-attention because of the optional chain.
Therefore:
If you try to obtain the Int value through the optional chain, no matter how many layers of links are used, the returned value is always Int ?. Similarly, if you try to obtain an Int through the optional chain? Value, no matter how many layers of links are used, the returned value is always Int ?.
The following example attempts to obtain the street attribute of address in john's residence attribute. Two optional links are used to contact the residence and address attributes. Both of them are optional:
If let johnsStreet = john. residence ?. Address ?. Street {println ("John's street name is \ (johnsStreet ). ")} else {println (" Unable to retrieve the address. ")} // print" Unable toretrieve the address. ".
The value of john. residence now contains a Residence instance, but john. residence. address is now nil, so john. residence ?. Address ?. Street call failed.
The preceding example shows that you are trying to obtain the street property value. The type of this attribute is String ?. Therefore, although two optional links are used before the optional type attribute, john. residence ?. Address ?. The Return Value Type of street is also String ?.
If you set an instance for Address as the value of john. residence. address and set an actual value for the street attribute of address, you can obtain the attribute value through multiple layers of optional chains.
Let johnsAddress = Address () johnsAddress. buildingName = "TheLarches" johnsAddress. street = "Laurel lstreet" john. residence !. Address = johnsAddress if let johnsStreet = john. residence ?. Address ?. Street {println ("John's street name is \ (johnsStreet ). ")} else {println (" Unable to retrieve the address. ")} // print" John 'sstreet name is Laurel Street. ".
It is worth noting that "!" When defining the address instance (john. residence. address ). The john. residence attribute is an optional type, so you need to use it before getting the address attribute! Parse to obtain its actual value.
Method for judging the return value by link
The previous example explains how to obtain optional type attribute values through the optional chain. You can also call the method that returns the optional type value and link the returned value of the method as needed.
The following example calls the buildingIdentifier method in the Address class through the optional chain. The Return Value Type of this method is String ?. As mentioned above, the final return value type of this method after the optional chain call is still String? :
If let buildingIdentifier = john. residence ?. Address ?. BuildingIdentifier () {println ("John's building identifier is \ (buildingIdentifier).")} // print "John's building identifier is The Larches .".
If you want to further execute the optional link for the method return value, place the optional link question mark after the method brackets:
If let upper = john. residence ?. Address ?. BuildingIdentifier ()?. UppercaseString {println ("John's uppercase building identifier is \ (upper).")} // print "John's uppercasebuilding identifier is the larches .".
Note: In the preceding example, you put the optional question mark after the brackets because the optional value you want to link is the returned value of the buildingIdentifier method, not the buildingIdentifier method itself.