苹果Swift语言官方文档(中文翻译版)
本文档由长沙戴维营教育组织翻译和校对,由于英语水平有限,请大家指正。
长沙戴维营教育还为本教程录制了配套的视频教程在乌班图学院上免费提供,欢迎大家一起学习。
下面的章节,如果为蓝色链接表示以及翻译完毕,可以查看,如果为黑色则表示正在紧张的翻译中。本文档中文版每天都会更新,大家可以随时查看。如由Bug欢迎改正。
类型转换
类型转换是一种检查对象类型的方法,它能够将一个类型的实例在它所在的类层次结构上,看作是它的父类或子类对象进行使用。
Swift使用is和as来实现类型转换。这两个操作符提供了一个简单的方式来检查某个值的类型或将其作为其它的数据类型使用。
我们还可以用类型转换来检查某个类型是否实现了一个协议,详情请参考“检查协议的实现”。
为类型转换定义类层次结构
我们可以使用类型转换在一个类层次结构里检查对象的类型并将它转换为另外的类型。下面的三个代码片段定义了一个类层次结构以及一个包含这些类的三个实例的数组。下面用它们来测试类型转换。
第一个代码片段定义了一个新的基类MediaItem。这个类为一个数字媒体库中的条目提供基本功能。它声明了一个String类型的name属性和一个init name初始化方法。(这里假设所有的媒体条目,包括电影和歌曲在内都有名字)
class Mediaitem {var name: Stringinit(name: String) {self.name = name}}
第二个片段定义了MediaItem的两个子类。其中第一个子类Movie封装了电影的信息,它增加了director属性和对应的初始化方法。第二个类Song增加了artist属性和对应的初始化方法。
class Movie: Mediaitem {var director: Stringinit(name: String, director: String) {self.director = directorsuper.init(name: name)}}class Song: Mediaitem {var artist: Stringinit(name: String, artist: String) {self.artist = artistsuper.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[]
类型检查
Swift使用is操作符检查一个实例是否是某个类型的子类。运算结果会返回true或false。下面的例子定义两个变量movieCount和songCount,并统计library中Movie和Song对象的数目。
var movieCount = 0var songCount = 0for 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"
上面的例子使用for-in遍历library数组,并将元素取出来存放在item常量中。如果item is Movie返回true,当前的MediaItem对象是一个Moive类型的实例,反之则不是。同样的,使用item is Song来判断是否为Song类型的实例。循环结束后,movieCount和songCount里存放的就是数组中Movie和Song类型对象的数目。
向下类型转换
某个类型的常量或变量有可能实际引用的是它的子类的对象。在这种情况下,可以用as操作符将它转换为对应的子类类型。
由于向下类型转换可能会失败,类型转换符有两种不同的形式。第一种是选项形式(Optional)as?,它返回一个目标类型的选项值。第二种是强制形式as,尝试将转换和结果的解包操作在同一个操作中完成。
如果我们不能确定向下类型转换是否成功,可以使用选项形式as?。这种形式总是返回一个选项值,如果转换失败的话,它会包含nil值。这样我们就可以检查类型转换是否成功。
只有当我们能够确定类型转换会成功的时候才使用强制类型转as。如果转换失败的话,它会抛出一个运行是错误。
下面的例子遍历library中的每个值,并且打印出它们的描述信息。为了访问director或artist属性,我们需要确保它们是Moive或者Song类型的实例。
在这个例子里,我们并没有提前知道library里的值是什么类型,因此使用选项形式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
上面的例子先尝试将它转换为Movie,如果转换失败,则尝试Song类型。item as Movie的结果是Movie?,或者叫可选的Movie类型。上面用选项是否包含值来判断类型转换是否成功。if let movie = item as? Movie读作:
“尝试将item按Movie类型进行访问,如果访问成功,将Movie选项中的值设置给一个Movie类型的常量movie“
如果类型转换成功,movie就可以用来打印Movie对象的描述,它包含director的名字。同样的可以将Song类型的值取出来并打印它们的描述。
提示 类型转换不会改变对象的值。它只是将这个对象作为另外一个类型的对象使用。
Any和AnyObject类型转换
Swift提供了两个特殊的类型来表示没有特定类型。
AnyObject能表示任意任意类类型的对象。Any可以表示除函数类型以外的所有类型的值。
提示 只有当我们确切需要
Any和AnyObject的能力时才使用它们。一般情况下都应该明确一个特定的数据类型。
AnyObject
在Swift中调用Cocoa的API时,通常都用AnyObject[]来接收数组的值。这是因为Objective-C的数组里能够存放任意的对象类型。不过一般情况下我们都能够从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 Movieprintln("Movie: '\(movie.name)', dir. \(movie.director)")}// Movie: '2001: A Space Odyssey', dir. Stanley Kubrick// Movie: 'Moon', dir. Duncan Jones// Movie: 'Alien', dir. Ridley Scott
下面有更简洁的写法:
for movie in someObjects 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
Any
下面的例子使用Any来应付具有多种数据类型,甚至包含非类类型数据的情况。例子里创建了一个things数组,存放Any类型的数据:
var 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数组包含两个Int值、两个Double值、一个String值、一个元组(Double, Double)和一个Movie对象。
我们使用is和as操作符来判断对象的类型。下面的代码遍历things数组,并使用switch语句进行判断。下面的一些case语句将things中的值赋值给一个特定类型的常量,方便对它们进行打印:
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)")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
提示 在
switch语句中使用as而不是as?来进行类型检查和转换总是安全的。
