自判断链接(Optional Chaining)是一种可以请求和调用属性、方法及子脚本的过程,它的自判断性体现于请求或调用的目标当前可能为空(nil)。如果自判断的目标有值,那么调用就会成功;相反,如果选择的目标为空(nil),则这种调用将返回空(nil)。多次请求或调用可以被链接在一起形成一个链,如果任何一个节点为空(nil)将导致整个链失效。

注意: Swift的自判断链和Objective-C中的消息为空有些相像,但是Swift可以使用在任意类型中,并且失败与否可以被检测到。

自判断链接可替代强制拆包

通过在想调用的属性、方法、或子脚本的自判断值(optional value)(非空)后面放一个问号,可以定义一个自判断链接。这一点很像在自判断值后面放一个声明符号来强制拆得其封包内的值。他们的主要的区别在于当自判断值为空时自判断链接即刻失败,然而一般的强制拆包将会引发运行时错误。

为了反映自判断链接可以调用空(nil),不论你调用的属性、方法、子脚本等返回的值是不是自判断值,它的返回结果都是一个自判断值。你可以利用这个返回值来检测你的自判断链接是否调用成功,有返回值即成功,返回nil则失败。

调用自判断链接的返回结果与原本的返回结果具有相同的类型,但是原本的返回结果被包装成了一个自判断值,当自判断链接调用成功时,一个应该返回Int的属性将会返回Int?。

下面几段代码将解释自判断链接和强制拆包的不同。

首先定义两个类Person和Residence。

 
  1. class Person {
  2. var residence: Residence?
  3. }
  4. class Residence {
  5. var numberOfRooms = 1
  6. }

Residence具有一个Int类型的numberOfRooms,其值为1。Person具有一个自判断residence属性,它的类型是Residence?。

如果你创建一个新的Person实例,它的residence属性由于是被定义为自判断型的,此属性将默认初始化为空:

 
  1. let john = Person()

如果你想使用声明符!强制拆包获得这个人residence属性numberOfRooms属性值,将会引发运行时错误,因为这时没有可以供拆包的residence值。

 
  1. let roomCount = john.residence!.numberOfRooms
  2. // this triggers a runtime error”
  3. //将导致运行时错误

当john.residence不是nil时,会运行通过,且会将roomCount 设置为一个int类型的合理值。然而,如上所述,当residence为空时,这个代码将会导致运行时错误。

自判断链接提供了一种另一种获得numberOfRooms的方法。利用自判断链接,使用问号来代替原来!的位置:

 
  1. if let roomCount = john.residence?.numberOfRooms {
  2. println("John's residence has \(roomCount) room(s).")
  3. } else {
  4. println("Unable to retrieve the number of rooms.")
  5. }
  6. // 打印 "Unable to retrieve the number of rooms.

这告诉Swift来链接自判断residence?属性,如果residence存在则取回numberOfRooms的值。

因为这种尝试获得numberOfRooms的操作有可能失败,自判断链接会返回Int?类型值,或者称作“自判断Int”。当residence是空的时候(上例),选择Int将会为空,因此会出先无法访问numberOfRooms的情况。

要注意的是,即使numberOfRooms是非自判断Int(Int?)时这一点也成立。只要是通过自判断链接的请求就意味着最后numberOfRooms总是返回一个Int?而不是Int。

你可以自己定义一个Residence实例给john.residence,这样它就不再为空了:

 
  1. john.residence = Residence()
  2. john.residence 现在有了实际存在的实例而不是nil了。如果你想使用和前面一样的自判断链接来获得numberOfRoooms,它将返回一个包含默认值1的Int?:
  3. if let roomCount = john.residence?.numberOfRooms {
  4. println("John's residence has \(roomCount) room(s).")
  5. } else {
  6. println("Unable to retrieve the number of rooms.")
  7. }
  8. // 打印 "John's residence has 1 room(s)"。

为自判断链接定义模型类

你可以使用自判断链接来多层调用属性,方法,和子脚本。这让你可以利用它们之间的复杂模型来获取更底层的属性,并检查是否可以成功获取此类底层属性。

后面的代码定义了四个将在后面使用的模型类,其中包括多层自判断链接。这些类是由上面的Person和Residence模型通过添加一个Room和一个Address类拓展来。

Person类定义与之前相同。

 
  1. class Person {
  2. var residence: Residence?
  3. }

Residence类比之前复杂些。这次,它定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组:

 
  1. class Residence {
  2. var rooms = Room[]()
  3. var numberOfRooms: Int {
  4. return rooms.count
  5. }
  6. subscript(i: Int) -> Room {
  7. return rooms[i]
  8. }
  9. func printNumberOfRooms() {
  10. println("The number of rooms is \(numberOfRooms)")
  11. }
  12. var address: Address?
  13. }

因为Residence存储了一个Room实例的数组,它的numberOfRooms属性值不是一个固定的存储值,而是通过计算而来的。numberOfRooms属性值是由返回rooms数组的count属性值得到的。

为了能快速访问rooms数组,Residence定义了一个只读的子脚本,通过插入数组的元素角标就可以成功调用。如果该角标存在,子脚本则将该元素返回。

Residence中也提供了一个printNumberOfRooms的方法,即简单的打印房间个数。

最后,Residence定义了一个自判断属性叫address(address?)。Address类的属性将在后面定义。 用于rooms数组的Room类是一个很简单的类,它只有一个name属性和一个设定room名的初始化器。

 
  1. class Room {
  2. let name: String
  3. init(name: String) { self.name = name }
  4. }

这个模型中的最终类叫做Address。它有三个自判断属性他们额类型是String?。前面两个自判断属性buildingName和 buildingNumber作为地址的一部分,是定义某个建筑物的两种方式。第三个属性street,用于命名地址的街道名:

 
  1. class Address {
  2. var buildingName: String?
  3. var buildingNumber: String?
  4. var street: String?
  5. func buildingIdentifier() -> String? {
  6. if buildingName {
  7. return buildingName
  8. } else if buildingNumber {
  9. return buildingNumber
  10. } else {
  11. return nil
  12. }
  13. }
  14. }

Address类还提供了一个buildingIdentifier的方法,它的返回值类型为String?。这个方法检查buildingName和buildingNumber的属性,如果buildingName有值则将其返回,或者如果buildingNumber有值则将其返回,再或如果没有一个属性有值,返回空。

通过自判断链接调用属性

正如上面“ 自判断链接可替代强制拆包”中所述,你可以利用自判断链接的自判断值获取属性,并且检查属性是否获取成功。然而,你不能使用自判断链接为属性赋值。

使用上述定义的类来创建一个人实例,并再次尝试后去它的numberOfRooms属性:

 
  1. let john = Person()
  2. if let roomCount = john.residence?.numberOfRooms {
  3. println("John's residence has \(roomCount) room(s).")
  4. } else {
  5. println("Unable to retrieve the number of rooms.")
  6. }
  7. // 打印 "Unable to retrieve the number of rooms。

由于john.residence是空,所以这个自判断链接和之前一样失败了,但是没有运行时错误。

通过自判断链接调用方法

你可以使用自判断链接的来调用自判断值的方法并检查方法调用是否成功。即使这个方法没有返回值,你依然可以使用自判断链接来达成这一目的。

Residence的printNumberOfRooms方法会打印numberOfRooms的当前值。方法如下:

 
  1. func printNumberOfRooms(){
  2. println(“The number of rooms is \(numberOfRooms)”)
  3. }

这个方法没有返回值。但是,没有返回值类型的函数和方法有一个隐式的返回值类型Void(参见Function Without Return Values)。

如果你利用自判断链接调用此方法,这个方法的返回值类型将是Void?,而不是Void,因为当通过自判断链接调用方法时返回值总是自判断类型(optional type)。,即使是这个方法本是没有定义返回值,你也可以使用if语句来检查是否能成功调用printNumberOfRooms方法:如果方法通过自判断链接调用成功,printNumberOfRooms的隐式返回值将会是Void,如果没有成功,将返回nil:

 
  1. if john.residence?.printNumberOfRooms() {
  2. println("It was possible to print the number of rooms.")
  3. } else {
  4. println("It was not possible to print the number of rooms.")
  5. }
  6. // 打印 "It was not possible to print the number of rooms."。

使用自判断链接调用子脚本

你可以使用自判断链接来尝试从子脚本获取值并检查子脚本的调用是否成功,然而,你不能通过自判断链接来设置子代码。

注意: 当你使用自判断链接来获取子脚本的时候,你应该将问号放在子脚本括号的前面而不是后面。自判断链接的问号一般直接跟在自判断表达语句的后面。

下面这个例子用在Residence类中定义的子脚本来获取john.residence数组中第一个房间的名字。因为john.residence现在是nil,子脚本的调用失败了。

 
  1. if let firstRoomName = john.residence?[0].name {
  2. println("The first room name is \(firstRoomName).")
  3. } else {
  4. println("Unable to retrieve the first room name.")
  5. }
  6. // 打印 "Unable to retrieve the first room name."。

在子代码调用中自判断链接的问号直接跟在john.residence的后面,在子脚本括号的前面,因为john.residence是自判断链接试图获得的自判断值。

如果你创建一个Residence实例给john.residence,且在他的rooms数组中有一个或多个Room实例,那么你可以使用自判断链接通过Residence子脚本来获取在rooms数组中的实例了:

 
  1. let johnsHouse = Residence()
  2. johnsHouse.rooms += Room(name: "Living Room")
  3. johnsHouse.rooms += Room(name: "Kitchen")
  4. john.residence = johnsHouse
  5. if let firstRoomName = john.residence?[0].name {
  6. println("The first room name is \(firstRoomName).")
  7. } else {
  8. println("Unable to retrieve the first room name.")
  9. }
  10. // 打印 "The first room name is Living Room."。


连接多层链接

你可以将多层自判断链接连接在一起,可以掘取模型内更下层的属性方法和子脚本。然而多层自判断链接不能再添加比已经返回的自判断值更多的层。 也就是说:

如果你试图获得的类型不是自判断类型,由于使用了自判断链接它将变成自判断类型。 如果你试图获得的类型已经是自判断类型,由于自判断链接它也不会提高自判断性。

因此,如果你试图通过自判断链接获得Int值,不论使用了多少层链接返回的总是Int?。 相似的,如果你试图通过自判断链接获得Int?值,不论使用了多少层链接返回的总是Int?。

下面的例子试图获取john的residence属性里的address的street属性。这里使用了两层自判断链接来联系residence和address属性,他们两者都是自判断类型:

 
  1. if let johnsstreet = john.residence?.address?.street {
  2. println("John's street name is \(johnsstreet).")
  3. } else {
  4. println("Unable to retrieve the address.")
  5. }
  6. // 打印 "Unable to retrieve the address.”。

john.residence的值现在包含一个Residence实例,然而john.residence.address现在是nil,因此john.residence?.address?.street调用失败。

从上面的例子发现,你试图获得street属性值。这个属性的类型是String?。因此尽管在自判断类型属性前使用了两层自判断链接,john.residence?.address?.street的返回值类型也是String?。

如果你为Address设定一个实例来作为john.residence.address的值,并为address的street属性设定一个实际值,你可以通过多层自判断链接来得到这个属性值。

 
  1. let johnsAddress = Address()
  2. johnsAddress.buildingName = "The Larches"
  3. johnsAddress.street = "Laurel Street"
  4. john.residence!.address = johnsAddress
  5. if let johnsstreet = john.residence?.address?.street {
  6. println("John's street name is \(johnsstreet).")
  7. } else {
  8. println("Unable to retrieve the address.")
  9. }
  10. // 打印 "John's street name is Laurel Street."。

值得注意的是,“!”符的在定义address实例时的使用(john.residence.address)。john.residence属性是一个自判断类型,因此你需要在它获取address属性之前使用!拆包以获得它的实际值。

链接自判断返回值的方法

前面的例子解释了如何通过自判断链接来获得自判断类型属性值。你也可以通过调用返回自判断类型值的方法并按需链接方法的返回值。

下面的例子通过自判断链接调用了Address类中的buildingIdentifier 方法。这个方法的返回值类型是String?。如上所述,这个方法在自判断链接调用后最终的返回值类型依然是String?:

 
  1. if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
  2. println("John's building identifier is \(buildingIdentifier).")
  3. }
  4. // 打印 "John's building identifier is The Larches."。

如果你还想进一步对方法返回值执行自判断链接,将自判断链接问号符放在方法括号的后面:

 
  1. if let upper = john.residence?.address?.buildingIdentifier()?.uppercaseString {
  2. println("John's uppercase building identifier is \(upper).")
  3. }
  4. // 打印 "John's uppercase building identifier is THE LARCHES."。

注意: 在上面的例子中,你将自判断链接问号符放在括号后面是因为你想要链接的自判断值是buildingIdentifier方法的返回值,不是buildingIdentifier方法本身。

swift之Optional Chaining的更多相关文章

  1. HTML5 WebSocket实现点对点聊天的示例代码

    这篇文章主要介绍了HTML5 WebSocket实现点对点聊天的示例代码的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. ios – 在Swift的UIView中找到UILabel

    我正在尝试在我的UIViewControllers的超级视图中找到我的UILabels.这是我的代码:这是在Objective-C中推荐的方式,但是在Swift中我只得到UIViews和CALayer.我肯定在提供给这个方法的视图中有UILabel.我错过了什么?我的UIViewController中的调用:解决方法使用函数式编程概念可以更轻松地实现这一目标.

  3. ios – 在Swift中将输入字段字符串转换为Int

    所以我非常擅长制作APP广告Swift,我试图在文本字段中做一些非常简单的输入,取值,然后将它们用作Int进行某些计算.但是’vardistance’有些东西不正确它是导致错误的最后一行代码.它说致命错误:无法解开Optional.None解决方法在你的例子中,距离是一个Int?否则称为可选的Int..toInt()返回Int?因为从String到Int的转换可能失败.请参阅以下示例:

  4. 如何在iOS中检测文本(字符串)语言?

    例如,给定以下字符串:我想检测每个声明的字符串中使用的语言.让我们假设已实现函数的签名是:如果没有检测到语言,则返回可选字符串.因此,适当的结果将是:有一个简单的方法来实现它吗?

  5. xamarin – 崩溃在AccountStore.Create().保存(e.Account,“);

    在Xamarin.Forms示例TodoAwsAuth中https://developer.xamarin.com/guides/xamarin-forms/web-services/authentication/oauth/成功登录后,在aOnAuthenticationCompleted事件中,应用程序在尝试保存到Xamarin.Auth时崩溃错误说不能对钥匙串说期待着寻求帮助.解决方法看看你

  6. ios – 将视频分享到Facebook

    我正在编写一个简单的测试应用程序,用于将视频从iOS上传到Facebook.由于FacebookSDK的所有文档都在Objective-C中,因此我发现很难在线找到有关如何使用Swift执行此操作的示例/教程.到目前为止我有这个在我的UI上放置一个共享按钮,但它看起来已禁用,从我读到的这是因为没有内容设置,但我看不出这是怎么可能的.我的getVideoURL()函数返回一个NSURL,它肯定包含视

  7. xcode – 错误“线程1:断点2.1”

    我正在研究RESTAPI管理器.这是一个错误,我无法解决它.我得到的错误在下面突出显示.当我打电话给这个班级获取资源时:我评论的线打印:Thread1:breakpoint2.1我需要修复错误的建议.任何建议都非常感谢解决方法您可能在不注意的情况下意外设置了断点.单击并拖动代表断路器外部断点的蓝色刻度线以将其擦除.

  8. ios – 更改导航栏标题swift中的字符间距

    类型的值有人可以帮我这个或建议一种不同的方式来改变swift中导航栏标题中的字符间距吗?解决方法您无法直接设置属性字符串.你可以通过替换titleView来做一个技巧

  9. ios – 如何从变量访问属性或方法?

    是否可以使用变量作为Swift中方法或属性的名称来访问方法或属性?在PHP中,您可以使用$object->{$variable}.例如编辑:这是我正在使用的实际代码:解决方法你可以做到,但不能使用“纯粹的”Swift.Swift的重点是防止这种危险的动态属性访问.你必须使用Cocoa的Key-ValueCoding功能:非常方便,它完全穿过你要穿过的字符串到属性名称的桥,但要注意:这里是龙.

  10. ios – 如果我将自动释放的对象桥接到Core Foundation,我必须使用__bridge或__bridge_retained吗?

    ARC迁移工具遇到了这个问题:特别是,它不确定它是否应该执行__bridge或__bridge_retained.而我也是.-fileURLWithPath返回一个自动释放的对象,在这个地方我不是fileURL的所有者.但与此同时,该对象的保留计数至少为1.我敢打赌,这只能用__bridge来完成.解决方法您只想为此使用常规__bridge强制转换.仅当您想要管理强制转换CF对象的生命周期时,才会使用__bridge_retained.例如:所以__bridge_retained确实告诉编译器你有一个AR

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部