[Swift learning] Swift programming tour-optional chain (21), swift tour

Source: Internet
Author: User

[Swift learning] Swift programming tour-optional chain (21), swift tour

Optional chain Optional Chaining is a type where the current value may benilTo request and call properties, methods, and methods of the following standard. If the optional value has a value, the call is successful. If the optional value isnil, Then the call will returnnil. Multiple calls can be connected to form a call chain. If any of the nodes isnil, The entire call chain will fail, that is, returnnil.

  

By placing a question mark (?), You can define an optional chain. This is like placing an exclamation mark (!) To forcibly expand its value. The main difference between them is that when the optional value is null, the optional chained call will only fail to be called, but forcible expansion will trigger a runtime error.

To reflect the optional chained call values (nil). No matter whether the property, method, or subscript returned value is an optional value, the returned result is an optional value. You can use this return value to determine whether your optional chained call is successful. If the call has a return value, the call is successful and the return value isnilThe call fails.

In particular, the returned results of the optional chained call have the same type as the original returned results, but are encapsulated into an optional value. For example, when an optional chained call is successful, if the original returned result of the attribute isIntType, it will becomeInt?Type.

The following sections of Code explain the differences between optional chained call and force expansion.

First define two classesPersonAndResidence:

class Person {    var residence: Residence?}class Residence {    var numberOfRooms = 1}

ResidenceThere isIntType attributenumberOfRoomsThe default value is1.PersonHas an optionalresidenceAttribute, whose type isResidence?.

If you create a newPersonInstance because of itsresidenceThe property is optional,johnAttribute will be initializednil:

let john = Person()

If you use an exclamation mark (!) Forcibly expand to obtain thisjohnOfresidenceAttributenumberOfRoomsValue.residenceNo value that can be expanded:

Let roomCount = john. residence !. NumberOfRooms // this will cause a running error

 

john.residenceNotnilValue, the above call will be successful, androomCountSetIntNumber of rooms of the type. As mentioned above, whenresidenceIsnilThe above code will trigger a runtime error.

Optional chained call provides another type of accessnumberOfRoomsBy using the question mark (?) To replace the original exclamation mark (!):

If let roomCount = john. residence ?. NumberOfRooms {print ("John's residence has \ (roomCount) room (s ). ")} else {print (" Unable to retrieve the number of rooms. ")} // print" Unable to retrieve the number of rooms."

InresidenceAfter a question mark is added, Swift willresidenceNotnilAccessnumberOfRooms.

Because accessnumberOfRoomsPossible failure. Optional chained call will returnInt?Type, or "optional"Int". As shown in the preceding example, whenresidenceIsnilOptionalIntThenilIndicates that the instance cannot be accessed.numberOfRooms. Optional when the access is successfulIntThe value is expanded by optional binding and assigned to non-optionalroomCountConstant.

Note that even ifnumberOfRoomsOptionalIntThis is also true. The optional chained call meansnumberOfRoomsReturnsInt?InsteadInt.

You canResidenceInstance assignedjohn.residenceIn this way, it is no longernilNow:

john.residence = Residence()

john.residenceNow contains an actualResidenceInstance, insteadnil. If you try to use the previous optional chain call for accessnumberOfRooms, Which now returns1OfInt?Type value:

If let roomCount = john. residence ?. NumberOfRooms {print ("John's residence has \ (roomCount) room (s ). ")} else {print (" Unable to retrieve the number of rooms. ")} // print" John's residence has 1 room (s )."
Define model classes for optional chained calls

You can call attributes, methods, and subscripts of multiple layers by using optional chained calls. In this way, you can access the sub-attributes in a complex model and determine whether the Sub-attributes, methods, or subscript can be accessed.

The following code defines four model classes, including multiple optional chain calls. For conveniencePersonAndResidenceAddedRoomClass andAddressClass and related attributes, methods, and subscripts.

PersonClass:

class Person {    var residence: Residence?}

ResidenceClass

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("The number of rooms is \(numberOfRooms)")    }    var address: Address?}

NowResidenceWith a storageRoomArray of instances,numberOfRoomsAttributes are implemented as computing attributes, rather than storage attributes.numberOfRoomsAttribute is returned simplyroomsArraycountAttribute Value.

ResidenceAlso provides accessroomsArray shortcut, that is, provide accessible subscriptroomsArray.

In addition,ResidenceAlso providesprintNumberOfRooms()Method. The function of this method is to printnumberOfRooms.

Finally,ResidenceAn optional attribute is also defined.address, Whose type isAddress?.AddressThe class definition is described below.

RoomA class is a simple class, and its instance is stored inroomsArray. This class contains only one attribute.nameAnd an initialization function to set this attribute to the appropriate room Name:

class Room {    let name: String    init(name: String) { self.name = name }}

 

The last class isAddressThis class has threeString?Type.buildingNameAndbuildingNumberProperty indicates the name and number of a building, and the third property.streetName of the street where the building is located:

class Address {    var buildingName: String?    var buildingNumber: String?    var street: String?    func buildingIdentifier() -> String? {        if buildingName != nil {            return buildingName        } else if buildingNumber != nil && street != nil {            return "\(buildingNumber) \(street)"        } else {            return nil        }    }}

AddressClass providesbuildingIdentifier()Method. The returned value isString?. IfbuildingNameReturn if there is a valuebuildingName. Or, ifbuildingNumberAndstreetReturns if all values exist.buildingNumber. Otherwise, returnnil.

 

Access attributes through optional chained calls

You can use an optional chained call to access its attributes on an optional value and determine whether the access is successful.

The following code createsPersonInstance, and then try to accessnumberOfRoomsAttribute:

Let john = Person () if let roomCount = john. residence ?. NumberOfRooms {print ("John's residence has \ (roomCount) room (s ). ")} else {print (" Unable to retrieve the number of rooms. ")} // print" Unable to retrieve the number of rooms."

 

Becausejohn.residenceIsnilSo this optional chain call will still fail as before.

You can also set the attribute value through optional chained calls:

let someAddress = Address()someAddress.buildingNumber = "29"someAddress.street = "Acacia Road"john.residence?.address = someAddress

 

In this examplejohn.residenceTo setaddressAttribute will also fail, becausejohn.residenceCurrentlynil.

The assignment process in the above Code is part of the optional chained call, which means that the Code on the right of the equal sign will not be executed when the optional chained call fails. For the above Code, it is difficult to verify this, because assigning a constant like this has no side effects. The following code completes the same thing, but it uses a function to createAddressInstance. This Function prints "Function was called" before the return, which enables you to verify whether the code on the right of the equal sign is executed.

func createAddress() -> Address {    print("Function was called.")    let someAddress = Address()    someAddress.buildingNumber = "29"    someAddress.street = "Acacia Road"    return someAddress}john.residence?.address = createAddress()

 

No messages are printed.createAddress()The function is not executed.

Call a method using an optional chain

You can call a method through an optional chained call and determine whether the call is successful, even if the method does not return a value.

ResidenceClassprintNumberOfRooms()Method to print the currentnumberOfRoomsValue, as shown below:

func printNumberOfRooms() {    print("The number of rooms is \(numberOfRooms)")}

This method does not return values. However, methods without return values have implicit return types.Void. This means that methods without return values will also return()Or empty tuples.

If you call this method through optional chained calls on optional values, the return type of this method will beVoid?InsteadVoidBecause the returned values obtained through optional chained calls are optional. In this way, we can useifStatement to determine whether the call can be successfulprintNumberOfRooms()Method, even if the method itself does not define the return value. Determine whether the returned value isnilYou can determine whether the call is successful:

If john. residence ?. PrintNumberOfRooms ()! = Nil {print ("It was possible to print the number of rooms. ")} else {print (" It was not possible to print the number of rooms. ")} // print" It was not possible to print the number of rooms."

 


Similarly, you can determine whether the attribute value is successfully assigned through an optional chained call. In the above, we tryjohn.residenceInaddressAttribute assignment, even ifresidenceIsnil. An optional chained call to assign a value to an attribute will returnVoid?To determine whether the returned value isnilYou can see whether the assignment is successful:
If (john. residence ?. Address = someAddress )! = Nil {print ("It was possible to set the address. ")} else {print (" It was not possible to set the address. ")} // print" It was not possible to set the address."

 

Access subscript through optional chain calls

Through optional chained calls, we can access the subscript on an optional value and determine whether the subscript call is successful.

Note:
When you access the lower mark of an optional value through an optional chained call, you should put the question mark before the subscript square brackets instead of the following. The question mark of an optional chained call is generally followed by an optional expression.

The following example uses subscript to accessjohn.residenceAttribute StorageResidenceInstanceroomsThe name of the first room in the array, becausejohn.residenceIsnilSo the subscript call failed:

If let firstRoomName = john. residence? [0]. name {print ("The first room name is \ (firstRoomName ). ")} else {print (" Unable to retrieve the first room name. ")} // print" Unable to retrieve the first room name."

 

In this example, the question mark is placed directly injohn.residenceAnd in front of square brackets, becausejohn.residenceIs an optional value.

Similarly, you can assign values by subscript and using optional chained calls:

john.residence?[0] = Room(name: "Bathroom")

 

This assignment will also fail becauseresidenceCurrentlynil.

If you createResidenceAndroomsAdd someRoomInstanceResidenceInstance assignedjohn.residenceThen you can access the elements in the array through optional links and subscript:

Let johnsHouse = Residence () johnsHouse. rooms. append (Room (name: "Living Room") johnsHouse. rooms. append (Room (name: "Kitchen") john. residence = johnsHouseif let firstRoomName = john. residence? [0]. name {print ("The first room name is \ (firstRoomName ). ")} else {print (" Unable to retrieve the first room name. ")} // print" The first room name is Living Room."

 

Access optional subscript

If the subscript returns an optional type value, for example, in SwiftDictionaryThe subscript of A type key. You can place a question mark after the end of the lower mark to perform optional chained calls on its optional return values:

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

 

The preceding example definestestScoresArray, contains two key-value pairsStringType key ing TOIntValue array. This example uses optional chain call"Dave"The first element in the array is set91"Bev"The first element of the array.+1And then try"Brian"The first element in the array is set72. The first two calls are successful becausetestScoresThe dictionary contains"Dave"And"Bev"These two keys. HowevertestScoresNo in the dictionary"Brian"This key, so the third call failed.

Multi-layer optional chain call

You can connect multiple optional chain calls to access attributes, methods, and subscripts at a deeper model level. However, multi-layer optional chained calls do not increase the optional hierarchy of returned values.

That is to say:

  • If the accessed value is not optional, the optional chained call will return an optional value.
  • If the value you access is optional, the optional chained call does not make the optional return value "more optional ".

Therefore:

  • AccessIntValue.Int?, No matter how many layers of optional chained calls are used.
  • Similarly, access through optional chain callsInt?Value.Int?Value, and does not returnInt??.

The following example attempts to accessjohnInresidenceAttributeaddressAttributestreetAttribute. Two optional chain calls are used here,residenceAndaddressOptional values:

If let johnsStreet = john. residence ?. Address ?. Street {print ("John's street name is \ (johnsStreet ). ")} else {print (" Unable to retrieve the address. ")} // print" Unable to retrieve the address."

 

john.residenceNow include a validResidenceInstance. However,john.residence.addressThe current value isnil. Thereforejohn.residence?.address?.streetWill fail.

Note that, in the above example,streetIsString?.john.residence?.address?.streetThe return value is stillString?, Even if two optional chain calls are used.

Ifjohn.residence.addressAssign a valueAddressAndaddressInstreetSet a valid value for the property so that we can access it through optional chained calls.streetAttribute:

Let johnsAddress = Address () johnsAddress. buildingName = "The Larches" johnsAddress. street = "Laurel Street" john. residence ?. Address = johnsAddressif let johnsStreet = john. residence ?. Address ?. Street {print ("John's street name is \ (johnsStreet ). ")} else {print (" Unable to retrieve the address. ")} // print" John's street name is Laurel Street."

 

In the preceding examplejohn.residenceContains a validResidenceInstancejohn.residenceOfaddressAttribute assignment is successful.

Perform optional chained calls on optional return values of methods

The preceding example shows how to obtain the attribute value of an optional value through an optional chained call. We can also call methods by optional chained calls on an optional value, and continue to perform optional chained calls on the optional return values of methods as needed.

In the following example, callAddressOfbuildingIdentifier()Method. This method returnsString?Type value. As mentioned above, the final return value will still beString?Type:

If let buildingIdentifier = john. residence ?. Address ?. BuildingIdentifier () {print ("John's building identifier is \ (buildingIdentifier).")} // print "John's building identifier is The Larches ."

 

If you want to perform an optional chained call on the return value of the method, add a question mark after the parentheses of the method:

If let beginsWithThe = john. residence ?. Address ?. BuildingIdentifier ()?. HasPrefix ("The") {if beginsWithThe {print ("John's building identifier begins with \" \". ")} else {print (" John's building identifier does not begin with \ "\". ") }}// print" John's building identifier begins with ""."

 

Note:
In the above example, the question mark is added after the parentheses of the method because you needbuildingIdentifier()The optional return value of the method, rather than the method itself.

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.