造型

造型是检查一个实例的类型,并且/或者将这个实例作为它所处类继承谱系中的超类或者子类处理 的一种方式。

Swift的造型通过is和as操作符实现。这两个操作符提供了一种简单而且方便的方式检查一个值的类型或者将一个值造型为另外一种类型。

也可以使用操作来检查一个类型是否遵循了一个协议,就像 Checking for Protocol Conformance 一节的描述。

为造型定义一个类的谱系

可以对一个类谱系中的类或者子类采用造型来检查一个特定类实例的类型或者将特定类型的实例造型为谱系中的另外一个类。下面的三个代码片段定义了一个类的谱系和一个包含这些类实例的数组,用作造型的示例。

第一个代码片段定义了一个新的基本类叫做MediaItem。这个类提供了在数字媒体图书馆中存在的任何种类藏品的基本功能。它定义了一个String类型的name属性和一个init name构造方法。(这里假象所有的媒体藏品都,包括了电影和歌曲,都会有一个名字。)

class​ ​MediaItem​ {
​    ​var​ ​name​: ​String
​    ​init​(​name​: ​String​) {
​        ​self​.​name​ = ​name
​    }
​}

接下来的代码片段定义了两个MediaItem的子类。第一个子类Movie,封装了关于电影或者胶片的额外信息。它在MediaItem基础上添加了一个director属性,还有一个和超类吻合的构造方法。第二个子类,Song,在其超类上添加了一个artist属性和一个构造方法:

class​ ​Movie​: ​MediaItem​ {
​    ​var​ ​director​: ​String
​    ​init​(​name​: ​String​,​director​: ​String​) {
​        ​self​.​director​ = ​director
​        ​super​.​init​(​name​: ​name​)
​    }
​}
​
​class​ ​Song​: ​MediaItem​ {
​    ​var​ ​artist​: ​String
​    ​init​(​name​: ​String​,​artist​: ​String​) {
​        ​self​.​artist​ = ​artist
​        ​super​.​init​(​name​: ​name​)
​    }
​}

最后一个代码片段创建了一个常量的数组叫做library,它其中包含两个Movie实例和三个Song实例。透过这个数组的内容的字面初始化过程可以推断出library数组的类型。Swift的类型检查会推断Movie和Song有一个共同的超类MediaItem,所以会推断library数组的类型是[MediaItem]:

​let​ ​library​ = [
​    ​Movie​(​name​: ​"Casablanca"​,​director​: ​"Michael Curtiz"​),​    ​Song​(​name​: ​"Blue Suede Shoes"​,​artist​: ​"Elvis Presley"​),​    ​Movie​(​name​: ​"Citizen Kane"​,​director​: ​"Orson Welles"​),​    ​Song​(​name​: ​"The One And Only"​,​artist​: ​"Chesney Hawkes"​),​    ​Song​(​name​: ​"Never Gonna Give You Up"​,​artist​: ​"Rick Astley"​)
​]
​// the type of "library" is inferred to be [MediaItem]

library中存储的内容实质上仍然是Movie和Song的实例。但是,如果遍历这个数组中的内容,获得到的内容类型被当作MediaItem,而不是Movie或者Song。为了在它们的原始类型基础上进行工作,需要检查它们的类型或者将它们向下造型为不同的类型(像上面描述的)。

类型检查

使用类型检查操作符(is)来检查一个实例是否是一个特定的子类型。类型检查操作符在实例是那个子类的时候返回true,反之则返回false。

下面的例子定义了两个变量,movieCount和songCount,它们俩个分别计算Movie和Song实例的在library数组中的个数:

var​ ​movieCount​ = ​0
​var​ ​songCount​ = ​0
​
​for​ ​item​ ​in​ ​library​ {
​    ​if​ ​item​ ​is​ ​Movie​ {
​        ++​movieCount
​    } ​else​ ​if​ ​item​ ​is​ ​Song​ {
​        ++​songCount
​    }
​}
​
​println​(​"Media library contains ​\(​movieCount​)​ movies and ​\(​songCount​)​ songs"​)
​// prints "Media library contains 2 movies and 3 songs"

这个例子中遍历了所有library数组中的内容。每次for-in循环中,会将下数组中的下一个MediaItem赋值给常量item。

如果当前的MediaItem是一个Movie实例,那么item is Movie 返回true,反之返回false。类似的,item is Song 检查当前内容是否是一个Song实例。在for-in循环的末尾,movieCount和songCount包含了每种类型内容在数组中被发现的个数。

向下造型

在后台,一个特定类的常量或者变量可能指向一个这个类子类。如果是这种情况,可以通过造型操作符(type case operator)(as?或者as!)将其造型为子类。

因为向下造型可能会失败,造型操作符有两种不同的方式。可选方式,as?,返回一个可选的想要向下造型的类型。强制方式,as!,尝试将向下造型而且强制拆包结果作为一个单一的复合操作。

当不能确信向下造型一定会成功时,使用可选方式的造型操作符(as?)。可选模式始终会返回一个可选值,如果造型失败返回值将是nil。这样可以检查造型是否成功。

只有当可以确信向下造型一定会成功时,使用强制方式的造型操作符(as!)。当向下造型的目标类型不对时,这种方式会触发一个运行时错误。

下面的例子遍历了library数组中的每个MediaItem,并且将每个内容中的对应描述打印了出来。为了做到这点,需要对每个内容作为Movie或者Song进行访问,而不是作为MeidaItem。内容的描述中用到了它们特有的属性,所以必须要能够访问Movie的director属性或者Song的artist属性。

这个例子中,数组中的每一项,可能是Movie,也可能是Song。你不知道每一项的更多信息,使用可选方式的造型操作符(as?)在每次循环中检查向下造型的做法是合适的:

for​ ​item​ ​in​ ​library​ {
​    ​if​ ​let​ ​movie​ = ​item​ ​as​? ​Movie​ {
​        ​println​(​"Movie: '​\(​movie​.​name​)​',dir. ​\(​movie​.​director​)​"​)
​    } ​else​ ​if​ ​let​ ​song​ = ​item​ ​as​? ​Song​ {
​        ​println​(​"Song: '​\(​song​.​name​)​',by ​\(​song​.​artist​)​"​)
​    }
​}
​
​// Movie: 'Casablanca',dir. Michael Curtiz
​// Song: 'Blue Suede Shoes',by Elvis Presley
​// Movie: 'Citizen Kane',dir. Orson Welles
​// Song: 'The One And Only',by Chesney Hawkes
​// Song: 'Never Gonna Give You Up',by Rick Astley

例子一开始是这将当前的item向下造型为Movie。因为item是一个MediaItem实例,所以它可能是个Movie;同样它也可能是个Song,或者只是 一个MediaItem。因为这些不确定性,所以as?方式的造型操作符在试图向下造型为一个子类时返回一个可选值。item as Movie返回一个Movie?或者说是“可选的Movie”。

当碰到liberary书中的一个Song实例时,向下造型为Movie会失败。为了解决这个问题,上面的例子使用了可选绑定来检查可选Movie实际包含的值(其实就是检查向下造型是否成功。)这个可选绑定写作“if let movie = item as? Movie”它的意思是:
“试着将item作为Movie访问。如果成功,用返回的可选Movie中的值给临时常量moive赋值。”

如果造型成功,movie中的属性被用来打印那个Movie实例的描述,其中包括了它的director的名字。当一个Song被找到,同样的规则在检查Song实例时也被使用,也会打印出对应的描述(包括artist名字)。

NOTE
造型实际上没有改变实例也没有改变实例的值。实例仍然是实例,只不过被当作造型的目标类型处理和访问了而已。

对Any和AnyObject造型

Swift提供了两个特殊的类型别称表示没有指定的类型:

AnyObject可以表示任意类的实例。
Any竟然可以表示任意实例,包括函数类型。

NOTE
属实需要使用Any和AnyObject的行为或者功能时才可以使用它们。通常,在代码中指定类型比较好。

任何对象(AnyObject)

在使用Cocoa API的时候,经常会接收到类型是[AnyObject]的数组,或者叫做“一个值是任意对象类型的数组”。这是因为OC语言没有明确类型的数组。但是,经常根据API提供的信息可以确信那个数组中含有某些特定的对象。

这种情况下,可以使用强制版本的造型操作符(as)对数组中的每一项都造型为特定的类型而不再使用AnyObject,这样做不需要可选类型解包。

下面的例子定义了一个[AnyObject]类型的数组,用Movie的三个实例填充了它:

​let​ ​someObjects​: [​AnyObject​] = [
​    ​Movie​(​name​: ​"2001: A Space Odyssey"​,​director​: ​"Stanley Kubrick"​),​    ​Movie​(​name​: ​"Moon"​,​director​: ​"Duncan Jones"​),​    ​Movie​(​name​: ​"Alien"​,​director​: ​"Ridley Scott"​)
​]

因为知道这个数组中之包含Movie实例,可以用强制版本的造型(as)操作符对其直接进行造型和拆包:

for​ ​object​ ​in​ ​someObjects​ {
​    ​let​ ​movie​ = ​object​ ​as​ ​Movie
​    ​println​(​"Movie: '​\(​movie​.​name​)​',dir. ​\(​movie​.​director​)​"​)
​}
​// Movie: '2001: A Space Odyssey',dir. Stanley Kubrick
​// Movie: 'Moon',dir. Duncan Jones
​// Movie: 'Alien',dir. Ridley Scott

这个循环的更简洁版本是,将someObjects 数组造型为[Movie]类型而不是对每个内容进行向下造型:

​for​ ​movie​ ​in​ ​someObjects​ ​as​ [​Movie​] {
​    ​println​(​"Movie: '​\(​movie​.​name​)​',dir. Ridley Scott

任何(Any)

这里的例子使用了Any来处理一个不同类型的混合体,其中包括了函数类型和不是类的类型。这个例子创建了一个数组叫做things,它其中存储了Any类型的值:

ar​ ​things​ = [​Any​]()
​
​things​.​append​(​0​)
​things​.​append​(​0.0​)
​things​.​append​(​42​)
​things​.​append​(​3.14159​)
​things​.​append​(​"hello"​)
​things​.​append​((​3.0​,​5.0​))
​things​.​append​(​Movie​(​name​: ​"Ghostbusters"​,​director​: ​"Ivan Reitman"​))
​things​.​append​({ (​name​: ​String​) -> ​String​ ​in​ ​"Hello,​\(​name​)​"​ })

tings数组包含两个Int值、两个Double值、一个String值,一个{Double,Double}类型的元组、电影“Ghostbusters”还有一个带一个String值参数返回另一个String值的臂包表达式。

可以在switch的case语句中使用is和as操作符,从仅仅已知为Any或者AnyObject类型的常量或者变量中发现特定的类型。下面的例子遍历了things数组的每一项,同时用一个switch语句查询每一项的类型。若干个switch的case语句将匹配到的值绑定到特定类型的常量,进而可以将值打印出来:

for​ ​thing​ ​in​ ​things​ {
​    ​switch​ ​thing​ {
​    ​case​ ​0​ ​as​ ​Int​:
​        ​println​(​"zero as an Int"​)
​    ​case​ ​0​ ​as​ ​Double​:
​        ​println​(​"zero as a Double"​)
​    ​case​ ​let​ ​someInt​ ​as​ ​Int​:
​        ​println​(​"an integer value of ​\(​someInt​)​"​)
​    ​case​ ​let​ ​someDouble​ ​as​ ​Double​ ​where​ ​someDouble​ > ​0​:
​        ​println​(​"a positive double value of ​\(​someDouble​)​"​)
​    ​case​ ​is​ ​Double​:
​        ​println​(​"some other double value that I don't want to print"​)
​    ​case​ ​let​ ​someString​ ​as​ ​String​:
​        ​println​(​"a string value of \"​\(​someString​)​\""​)
​    ​case​ ​let​ (​x​,​y​) ​as​ (​Double​,​Double​):
​        ​println​(​"an (x,y) point at ​\(​x​)​,​\(​y​)​"​)
​    ​case​ ​let​ ​movie​ ​as​ ​Movie​:
​        ​println​(​"a movie called '​\(​movie​.​name​)​',dir. ​\(​movie​.​director​)​"​)
​    ​case​ ​let​ ​stringConverter​ ​as​ ​String​ -> ​String​:
​        ​println​(​stringConverter​(​"Michael"​))
​    ​default​:
​        ​println​(​"something else"​)
​    }
​}
​
​// zero as an Int
​// zero as a Double
​// an integer value of 42
​// a positive double value of 3.14159
​// a string value of "hello"
​// an (x,y) point at 3.0,5.0
​// a movie called 'Ghostbusters',dir. Ivan Reitman
​// Hello,Michael

NOTE switch语句中的case使用了强制版本的造型操作符(as,而不是as?)来检查和造型特定的类型。在switch 的case语句上下文中,这样的检查始终是安全的。

[翻译]Swift编程语言——造型的更多相关文章

  1. html5使用canvas实现弹幕功能示例

    这篇文章主要介绍了html5使用canvas实现弹幕功能示例的相关资料,需要的朋友可以参考下

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

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

  3. 前端实现弹幕效果的方法总结(包含css3和canvas的实现方式)

    这篇文章主要介绍了前端实现弹幕效果的方法总结(包含css3和canvas的实现方式)的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  4. H5 canvas实现贪吃蛇小游戏

    本篇文章主要介绍了H5 canvas实现贪吃蛇小游戏,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

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

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

  6. ios – parse.com用于键,预期字符串的无效类型,但是得到了数组

    我尝试将我的数据保存到parse.com.我已经预先在parse.com上创建了一个名为’SomeClass’的类.它有一个名为’mySpecialColumn’的列,其数据类型为String.这是我尝试使用以下代码保存数据的代码:如果我运行这个我得到:错误:密钥mySpecialColumn的无效类型,预期字符串,但得到数组这就是我在parse.com上的核心外观:有谁知道我为什么会收到这个错误?

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

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

  8. ios – Swift相当于`[NSDictionary initWithObjects:forKeys:]`

    Swift的原生字典是否与[NSDictionaryinitWithObjects:forKeys:]相当?假设我有两个带键和值的数组,并希望将它们放在字典中.在Objective-C中,我这样做:当然我可以通过两个数组迭代一个计数器,使用vardict:[String:Int]并逐步添加东西.但这似乎不是一个好的解决方案.使用zip和enumerate可能是同时迭代两者的更好方法.然而,这种方法

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

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

  10. ios – 上下文类型’NSFastEnumeration’不能与数组文字一起使用

    斯威夫特3,你会这样做吗?解决方法正如您所发现的,您不能使用as-casting将数组文字的类型指定为NSFastEnumeration.您需要找到一个符合NSFastEnumeration的正确类,在您的情况下它是NSArray.通常写这样的东西:

随机推荐

  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,所以编译器会报错,现在来一一解决。

返回
顶部