可空链式调用
可空链式调用是一种可以请求和调用属性、方法以及下标的过程,它的可空性体现于请求或调用的目标当前可能为空(nil)。如果可空的目标有值,那么调用就会成功;如果选择的目标为空(nil),那么这种调用将返回空(nil)。多个连续的调用可以被链接在一起形成一个调用链,如果其中任何一个节点为(nil)将导致整个链调用失败。
note:Swift的可空链式调用和Objective-c的消息为空有些想象,但是Swift可以使用在任意类型中,并且能够检查调用是否成功。
使用可空链式调用来强制展开
通过在想调用非空的属性、方法、或下标的可空值后面放一个问号,可以定义一个可空链。这一点很像在可空值后面放一个叹号(!)来强制展开其值。它们的主要的区别在于当可空值为空时可空链式只是调用失败,然而强制展开会触发运行时错误。
为了反映可空链式调用可以在空对象nil上调用,不论这个调用的属性、方法、下标等返回的值是不是可空值,它的返回结果都是一个可空值。我们可以利用这个返回值来判断我们的可空链式调用是否成功,如果调用有返回值则说明调用成功,返回nil则说明调用失败。
特别地,可空链式调用的返回结果与原本的返回结果具有相同的类型,但是被包装成了可空类型值。当可空链式调用成功时,一个本应该返回Int的类型的结果将会返回Int?类型。
class House {
var numberOfRooms = 1
}
class Person {
var house : House?
}
let john = Person()
//let num = john.house!.numberOfRooms
if let roomCount = john.house?.numberOfRooms {
print("John has \(roomCount) rooms")
}else{
print("John has no house")
}
为可空链式调用定义模型类
通过使用可空链式调用可以调用多层属性,方法和下标。这样可以通过各种模型向下访问各种子属性。并且判断能否访问子属性,方法或下标。
通过可空链式调用访问属性
可以通过可空链式调用访问属性的可空值,并且判断访问是否成功。
通过可空链式调用来调用方法
可以通过可空链式调用来调用方法,并判断是否调用成功,即使这个方法没有返回值。如果在空值桑调用可空链式调用来调用这个方法,这个方法返回类型为Void?而不是Void。
通过可空链式调用来访问下标
通过可空链式调用,我们可以用下标来对可空值进行读取或写入,并且判断下标调用是否成功。
note:当通过可空链式调用访问可空值的下标的时候,应该将问号放在下标放括号的前面而不是后面。可空链式调用的问号一般直接跟在可控表达式的后面。
多层链接
可以通过多个个链接多个可空链式调用来向下访问属性,方法以及下标。但是多层可空链式调用不会添加返回值的可空性。
也就是说:
- 如果我们访问的值不是可空的,通过可空链式调用将会返回可空值。
- 如果我们访问的值已经是可空的,通过可空链式调用不会变得“更”可空。
因此:
- 通过可空链式调用访问一个Int值,将会返回Int?,不过进行了多次可空链式调用。
- 类似的,通过可空链式调用访问Int?值,并不会变得更加可空。
对返回可空值的函数进行链接
通过可空链式调用来获取可空属性值。我们还可以通过链式调用来调用返回可空值的方法,并且可以继续对可空值进行链接。
</br></br></br>
错误处理
错误处理是响应错误以及从错误中恢复的过程。Swift提供了在运行时对可恢复错误抛出,捕获,传送和操作的高级支持。
某些操作并不能总是保证执行所有代码都可以执行或总会产出有用的结果。可选类型用来表示值可能为空,但是当执行失败的时候,通常要去了解此次失败是由何引起,你的代码就可以做出与之相应的反应。
note:Swift中的错误处理涉及到错误处理模式,这会用到cocoa和Objecive-c中的NSError。
表示并抛出错误
在Swift中,错误用遵循ErrorType协议类型的值来表示。这个空协议表示一种可以用做错误处理的类型。Swift的枚举类型尤为适合塑造一组相关的错误情形,枚举的关联值还可以提供额外的信息,表示某些错误情形的性质。
enum VendingMachineError: ErrorType {
case InvalidSelection //选择无效
case InsufficientFunds(coninsNeeded: Int) //金额不足
case OutOfStock // 缺货
}
抛出一个错误会让我们对所发生的意外情况作出提示,表示正常的执行流程不能被执行下去。抛出错误使用throw关键字。
throw VedingMachineError.InsufficientFunds(coninsNeeded:5)
处理错误
某个错误被抛出时,那个地方的某部分代码必须要负责处理这个错误(比如纠正这个错误、尝试另外一种方式、或时给用户提示这个错误)。Swift中有4种处理错误的方式。我们可以把函数抛出的错误传递给调用此函数代码、用do-catch语句处理错误、将错误作为可选类型处理、或者断言此错误根本不会发生。
note:Swift中的错误处理和其他语言中的try,catch和throw的异常处理很像。和其他语言(包括OC)的异常处理不同的是,Swift中的错误处理并不涉及堆栈解退,这是一个计算昂贵的过程。就此而言,throw语句的性能特性是可以和return语句相当的。
1.用throwing函数传递错误
用throws关键字来标识一个可抛出错误的函数,方法或是构造器。在函数声明中的参数列表之后加上throws。一个标识了throws的函数被称作throwing函数。如果这个函数还有返回类型,throws关键字需要写在箭头(->)的前面。
func canThrowErrors() throws -> String
func cannotThrowErrors() -> String
throwing函数从其内部抛出错误,并传递到函数被调用时所在的区域中。
note:只有throwing函数可以传递错误。任何在某个非throwing函数内部抛出的错误只能在此函数内部处理。
enum VendingMachineError: ErrorType {
case InvalidSelection //选择无效
case InsufficientFunds(coninsNeeded: Int) //金额不足
case OutOfStock // 缺货
}
struct Item {
var price : Int
var count : Int
}
class Vendingmachine {
var inventory = ["water": Item(price:12,count: 7),
"coco": Item(price: 10, count: 4),
"orange":Item(price: 7, count: 11)
]
var coinsDeposited = 0
func dispenseSnack(snack: String){
print("Dispensing \(snack)")
}
func vend(itemnamed name : String) throws {
guard var item = inventory[name] else{
throw VendingMachineError.InvalidSelection
}
guard item.count > 0 else{
throw VendingMachineError.OutOfStock
}
guard item.price <= coinsDeposited else{
throw VendingMachineError.InsufficientFunds(coninsNeeded: item.price - coinsDeposited)
}
coinsDeposited -= item.price
item.count -= 1
inventory[name] = item
dispenseSnack(name)
}
}
因为vend(itemNamed:)方法会传递出它抛出的任何错误,在我们代码中调用它的地方必须要直接处理这些错误---使用do-catch语句,try?或try!,要么继续将这些错误传递下去。
func buyMostSnack(name:String,machine:Vendingmachine) throws {
try machine.vend(itemnamed: name)
}
因为vend(itemNamed:)方法能抛出错误,所以在调用它的时候在它前面加了try关键字。
2.用Do-Catch处理错误
可以使用一个do-catch语句运行一段闭包代码来做错误处理。如果在do语句中的代码抛出了一个错误,则这个错误会与catch语句做匹配来决定哪条语句能处理它。
do {
try expression
}catch pattern1{
}catch pattern2 where condition {
}
在catch后面写一个模式(pattern)来标示这个语句能处理什么样的错误。如果一条catch语句没带一个模式,那么这条语句可以和任何错误相匹配,并且把错误和一个名字为name的局部常量做绑定。
catch语句不必将do语句中代码所抛出的每个可能的错误都处理。如果没有一条catch字句来处理错误,错误就会传播到周围的作用域。然而错误还是必须要被某个周围饿作用域处理的---要么是一个外围的do-catch错误处理,要么是一个throwing函数的内部。
var machine = Vendingmachine()
machine.coinsDeposited = 8
do {
try buyMostSnack("coco", machine: machine)
}catch VendingMachineError.InvalidSelection{
print("InvalidSelection")
}catch VendingMachineError.OutOfStock {
print("OutOfStock")
}catch VendingMachineError.InsufficientFunds(let coinsNeeded){
print("InsufficientFunds please insert \(coinsNeeded)")
}
将错误转换成可选值
可以使用try?通过将其转换成一个可选值来处理错误。如果在评估try?表达式时一个错误被抛出,哪么这个表达式的值就是nil。
func someThrowingFunction() throws -> Int {
//.....
}
let x = try? someThrowingFunction()
let y: Int?
do {
y = try someThrowingFunction()
} catch {
y = nil
}
如果someThrowFunction()抛出一个错误,x和y的值是nil。否则 x 和 y 的值就是该函数的返回值。注意无论someThrowFuction()的返回值类型是什么类型,x 和 y 都是这个类型的可选类型。
使错误传递失败
有时候我知道某个throwing函数实际上在运行时是不会抛出错误的。在这种条件下,我们可以在表达式前面写try!来使错误传递失败,并把调用包装在一个运行时断言中来判定会不会有错误抛出。如果实际上确实抛出了错误,我们就会得到一个运行时错误。
let photo = try! loadImage("./Resources/John.png")
指定清理操作
可以使用defer语句在代码执行到要离开当前的代码段之前去执行一套语句。该语句让我们能够作一些应该被执行的必要清理工作,不管是以何种方式离开当前的代码段的 ---无论时由于抛出错误而离开,或是因为一条return或者break的类似语句。defer语句将代码的执行延迟到当前的作用域退出之前。该语句由defer关键字和要被延时执行的语句组成。延时执行的语句不会包含任何会将控制权移交语句外面的代码,例如一条break或是return语句,或是抛出一个错误。延迟执行的操作是按照它们被指定的相反顺序执行---意思是第一条defer语句中的代码执行是在第二条defer语句中代码被执行之后,以此类推
func test(){
print("test begin")
defer{
print("1")
}
defer{
print("2")
}
defer{
print("3")
}
defer{
print("4")
}
print("test end")
}
test()
类型转换
类型转换可以判断实例的类型,也可以将实例看做是其父类或子类的实例。
类型转换在Swift中使用 is 和 as 操作符实现。这两个操作提供了一种简单达意的方式去检查值的类型或者转换它的类型。
我们也可以用它来检查一个类是否实现了某个协议
定义一个类层次作为例子
我们可以将类型转换用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中其他类型。
class MediaItem {
var name:String
init(name:String){
self.name = name
}
}
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)
}
}
let arr = [
Movie(name: "one", director: "1"),
Song(name: "one", artist: "1"),
Movie(name: "two", director: "2"),
Movie(name: "three", director: "3"),
Song(name: "three", artist: "3")
]
检查类型
用类型检查操作符(is)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回true , 否则返回false 。
for item in arr {
if item is Movie {
print("\(item) is movie")
}
if item is Song {
print("\(item) is song")
}
//总是true
if item is MediaItem {
print("\(item) is MediaItem")
}
}
向下转型
某类型的一个常量或变量可能在幕后实际上属于一个子类。当确定这种情况下,我们可以尝试向下转到它的子类型,用类型转换符(as?或 as!)
因为向下转型可能会失败,类型转型操作符带有两种不同形式。
- 条件形式 as?返回一个你试图向下转成的类型的可选值。
- 强制式 as!把试图向下转型和强制解包结果作为一个混合动作。
只有当可以确定向下转型一定会成功时,才使用强制形式(as!)。当试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。
note:转换没有真的改变实例或它的值。潜在的根本的实例保持不变;只是简单地把它作为它被转成的类来使用。
for item in arr {
if let movie = item as? Movie{
print("movie:\(movie.name) and \(movie.director)")
}else if let song = item as? Song {
print("song:\(song.name) and \(song.artist)")
}
}
Any和AnyObject的类型转换
Swift为不确定类型提供了两种特殊类型别名:
- AnyObject可以代表任何class类型的实例。
- Any可以表示任何类型,包括方法类型(function types)
note:只有当我们明确的需要它的行为和功能时才使用Any和AnyObject。在我们的代码里使用我们期望的明确的类型总是更好的。
AnyObject类型
当在工作中使用Cocoa APIs,我们一般会接收一个[AnyObject]类型的数组,或者说“一个任何对象类型的数组”。这是因为Objective-c没有明确的类型化数组。但是,我们常常可以从API提供的信息中清晰地确定数组中对象的类型 。
在这些情况下,我们可以使用强制形式类型转换(as)来下转在数组中的每一项到比AnyObject更明确的类型,不需要可选解析。
let someObjects: [AnyObject] = [
Movie(name: "one", director: "1"),
Movie(name: "two", director: "2"),
Movie(name: "three", director: "3"),
Movie(name: "four", director: "4"),
Movie(name: "five", director: "5")
]
for object in someObjects {
let movie = object as! Movie
print("Movie:\(movie.name) and \(movie.director)")
}
为了变成一个更短的形式,下面转someObjects数组为[Movie]类型来替代下转数组中的每一项的方式。
for movie in someObjects as! [Movie] {
print("Movie:\(movie.name) and \(movie.director)")
}
Any类型
这里有个例子,使用Any类型来和混合的不同类型一起工作。包括方法类型和非class类型。它们创建了一个可以存储Any类型的数组things。
var things = [Any]()
things.append(0)
things.append(0.0)
things.append(42)
things.append(3.1415926)
things.append("hello")
things.append({(name:String) -> String in "hello \(name)"})
嵌套类型
枚举类型常被用于特定类或结构体的功能。也能够在多种变量类型的环境中,方便的定义通用类或结构体来使用,为了实现这种功能,Swift允许你定义嵌套类型,可以在枚举类型、类和结构体种定义支持嵌套的类型。
要在一个类型中嵌套另一个类型,将需要嵌套的类型的定义写在被嵌套类型的区域{}内,而且可以根据需要定义多级嵌套。
嵌套类型实例
在外部对嵌套类型的引用,以被嵌套类型的名字为前缀,加上所要引用的属性,方法等。
struct Student {
struct PersonInfo {
var name:String
var age = 0
var sex = 0
}
struct School {
var name:String
var address:String
}
var info:PersonInfo
var school:School
}
let stu = Student(info: Student.PersonInfo(name:"hello",age:10,sex:1), school: Student.School(name:"school",address:"address")
stu.school.hello()
stu.school.address
网友评论