swift是一个新的在iOS、watchOS和tvOS上开发APP的编程语言。但是,swift很多地方跟使用C和objective-c开发的体验很像。
swift提供了有关C和Objective-C中基础的类自己的版本,包括整型的Int,浮点值得Double跟Float,布尔值Bool,文本数据String。swift也提供了三个主要的集合类型的版本,Array、Set和Dictionary,像Collection Types中描述的一样。
和C一样,swift通过标识符名称使用变量来存储和引用值。swift也大量使用了不能改变值的变量。这就是常量,比C中的常量更强大的功能。在swift中,当你操作的值不需要改变时,常量的使用使代码更安全简介。
除了相似的类型,swift引入了在Objective-C中没有的先进类型,例如元祖。你可以使用元祖创建和传递一组值。从函数中可以把多个值当成一个组合值返回出来。
swift也引入了可选类型的值,处理某个值得缺失。可选类型代表“有一个值,并且等于x”或者根本没有这个值。使用可选类型和Objective-C中指针指向nil很像,但是他能与任何类型结合使用,不仅仅是类。可选类型不仅仅比Objective-C中指向nil的指针更安全更有变现力,他也是swift中许多强大特性的核心。
swift是一个类型安全的语言,该语言会帮助你弄清楚能使你的代码可以运行的值的类型。如果你的代码需要一个string,类型安全会阻止你错误的传一个Int类型的值。同样的,类型安全可以防止你传一个可选的string类型到需要非可选string类型的代码段中。类型安全帮助你在开发过程中尽可能早的捕捉并且修复错误。
常量与变量(Constants and Variables)
常量和变量将一个类型(例如数字10或者字符串“Hello”)的值与名字(像maximumNumberOfLoginAttempts或者welcomeMessage)联系在一起。常量的值一旦设置了就不能更改,变量的值可以随意更改。
声明常量和变量(Declaring Constants and Variables)
常量跟变量必须在使用前声明。使用let关键词声明常量,使用var关键词声明变量。下面的例子中,常量和变量用来记录用户尝试记录登录次数:
let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0这串代码意思是:
“声明一个名为maximumNumberOfLoginAttempts的常量,给它一个值10.然后声明一个变量命名为currentLoginAttempt,给他一个初始值0”
这个例子中,登录最大尝试次数声明为一个常量,因为最大的值不会修改。当前尝试次数声明为变量,因为这个值在每次尝试登陆失败后需要递增。
可以在一行中声明多个常量和多个变量,用逗号分隔开。
var x = 0.0, y = 0.0, z = 0.0如果代码中的储存值不会改变,通常使用let关键字将其声明为一个常量。只有在存储需要修改的值时使用变量。
类型标记(Type Annotations)
当声明一个常量或者变量的时候提供一个类型标记,明确常量或变量可以存储值得类型。类型标记与跟在常量或者变量后面冒号在一起,跟着一个空格,跟在要使用的类型的名称之后。
下面这个例子展示了命名为welcomeMessage的变量的类型标记,指明了变量可以存储String类型的值。
var welcomeMessage:String声明中的冒号意思是“..类型的..”,所以上面代码可以理解为:
“声明一个名为welcomeMessage的String类型的变量”
“of type String”短语意思是“可以存储任何String类型的值”。可以当成可以存储“某一类型的值”。
welcomeMessage变量现在可以正确的设成任何string值:
welcomeMessage = "Hello"可以在一行里连续声明多个相同类型的变量,使用逗号分隔开,在最后一个变量名后面写一个类型标记
var red,green,blue:Double注意 练习中写类型标记是非常好的。如果你在定义一个常量或者变量时提供了初始值,swift会推导出常量或者变量的类型,跟Type Safety and Type Inference中描述的一样。在上面welcomeMessage的例子中,没有提供初始值,所以变量welcomeMessage的类型是通过类型标记而不是从初始化值中推导出来的。
命名常量和变量(Naming Constants and Variables)
常量和变量的命名可以包括任何字母,包括Unicode字符。
let π = 3.14159
let 你好 = ”你好世界“
let 🐶🐂 = ”dogcow“常量跟变量命名不能包括空格,数学符号,箭头,私有Unicode下标值或者线框描绘的字符。命名不能以数字开头,即使数字可以出现在名称中任何位置。
一旦声明了一个类型的常量或者变量,不能再以相同名字声明一次,也不能用它存储其他类型的值。既不能将常量变成变量,也不能将变量改成常量。
如果需要使用swift关键字给常量或者变量命名,用它命名的时候使用反引号(‘)。除非完全没有选择了,无论如何不要使用关键词关键字命名。
var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome is now "Bonjour!"let languageName = "Swift"
languageName = "Swift++"
// This is a compile-time error: languageName cannot be changed.打印变量和常量(Printing Constants and Variables)
可以使用print(_:separator:terminator:) 函数打印常量和变量当前的值:
print(friendlyWelcome)
// Prints "Bonjour!"全局函数print(_:separator:terminator:)将一个或多个值恰当打印出来。Xcode中,例如,print(_:separator:terminator:)函数将输出打印在Xcode的控制面板里。separator和terminator参数有默认的值,所以当代用函数时可以忽略他们。默认情况下,函数在打印的最后一行添加一个break line。要想打印一个没有break line的值,传一个空字符串作为terminator--例如,print(somevalue,terminator:”“)。关于默认参数的信息,查看Default Parameter Values。
在长字符串中swift用字符串插入符来包括一个常量或者变量的名字作为占位符,通知swift用这个变量或者常量当前的值来代替它。将名字用括号包起来,在开括号前使用反斜线来转义。
print("The current value of friendlyWelcome is \(friendlyWelcome)")//
Prints "The current value of friendlyWelcome is Bonjour!"全部可以使用字符串插入符的选择都在String Interpolation中。
注释(Comments)
使用注释来把不执行的文本加到你的代码中,像一个笔记或提示一样。注释在编译器执行的时候会被swift编译器忽略。
swift中的注释和C中的注释很像。单行注释开头是两个斜杠(//)。
// This is a comment.多行注释开头是斜杠跟着一个星号(/*)并且以星号跟着斜杠结尾(*/):
/* This is also a commentbut is
written over multiple lines. */不像C中的多行注释,swift中的多行注释可以内嵌多个其他的多行注释。通过开头写一个多行注释块,然后开始第二个多行注释来写内嵌注释。在第一个块之后第二个块关闭。
/* This is the start of the first multiline
comment./* This is the second, nested
multiline comment. */This is the end of the first multiline comment. */内嵌注释可以让你更快速简单地注释大块代码,即使你的代码已经有了多行注释。
分号(Semicolons)
与其他语言不同,swift不需要你的代码里每一个语句后写分号(semicolon),如果你想的话,写也可以。不管怎样,如果你想在一行里写多个独立的语句,分号是必须的。
let cat = "🐱"; print(cat)
// Prints "🐱"整型(Integers)
整型是没有分数的全部数字,像43或-23.整型是带标记的(正数,零,负数)或者不带标记(正数,零)。
swift提供了8,16,32,64位形式的带标记和不带标记整型。这些整型跟着一个像C一样的命名规则,8位无符号的整型的类型是UInt8,32位有符号整型的类型是Int32.像swift所有类型一样,这些整型类型有首字母大写的名称。
整型边界(Integer Bounds)
可以通过每个整型类型的min和max属性获取他们的最大和最小值:
let minValue = UInt8.min // minValue is equal to 0, and is of type UInt8
let maxValue = UInt8.max // maxValue is equal to 255, and is of type UInt8这些属性的值是合适尺寸数字类型(像上面例子中的UInt8),所以可以在表达式中与其他相同类型的值一起使用。
Int
在大多数情况,代码中不需要选择特定尺寸的整型来用。swift提供了另外的整型类型,Int,具有和当前平台原始单词尺寸一样大的尺寸。
- On a 32-bit platform,
Intis the same size asInt32. - On a 64-bit platform,
Intis the same size asInt64.
除非你需要要使用指定尺寸的整型,一般在代码中使用Int作为整型类型。这有助于代码一致性跟通用性。即使在32位平台上,Int可以储存任何在-2,147,483,648跟2,147,483,647之间的值,对于许多整型区间是足够大了。
UInt
swift也提供了无符号整型类型,与当前平台原始的字尺寸一样大的尺寸:
- On a 32-bit platform,
UIntis the same size asUInt32. - On a 64-bit platform,
UIntis the same size asUInt64.
只有当你特定需要跟平台原始字大小一样大的无符号整型类型时使用UInt。如果不是这种情况,Int更合适,即使要被储存的值已知是非负的。统一的为整型值使用Int有助于代码的通用性,避免需要在不同数字类型间转换,匹配整型类型引用,像Type Safety and Type Inference中描述一样。
浮点类型-小数(Floating-Point Numbers)
浮点类型时有小数部分的数字,像3.14159,0.1和-273.15.
浮点类型代表了比整型类型更大范围的值,可以储存比Int更大或者更小的数字。swift提供了两个符号型浮点类型:
Doublerepresents a 64-bit floating-point number.Floatrepresents a 32-bit floating-point number.
Double有至少小数点后15位的精确度,Float的精确度至少有小数点后6位小数的精确值,使用合适的浮点类型取决于本地跟要使用的值得范围。两种类型都合适的情况下,Double更合适。
类型安全跟类型引用(Type Safety and Type Inference)
swift是一个类型安全语言。类型安全的语言鼓励你明确你的代码可以与什么类型的值工作。如果你的代码需要String类型,你不可以错误的传给他一个Int。
因为swift是一个类型安全的,在代码编译的时候回去检查,并且把不匹配的类型标成错误。这让你在开发过程中可以尽早的捕捉到并修复错误。
当你需要不同类型的值时类型检查可以帮助你避免错误。不过,这不意味着你不得不指定每一个声明的常量跟变量的类型。如果你没有指定你需要的值得类型,swift使用类型推导来计算出合适的类型。当编译你的代码时,类型推导可以让编译器自动的推导出特定表达式的类型,通过简单的检查你提供的值。
因为类型推导,swift需要比C或者Objective-C更少的类型声明。常量跟变量仍然明确的类型,但是更多的指定类型的工作为你做了。
当你使用一个初始值声明变量或者常量时,类型推导更有用。常常发生在你通过分配一个字面量来声明常量或者变量时。(一个字面量值是一个值,在你的源码中直接出现的,例如下面例子中的42跟3.14159)
例如,如果你分配了一个字面量42给一个新的常量,没有说它是什么类型,swift推测你希望常量是一个Int值,因为你已经使用一个像整型的值初始化了它:
let meaningOfLife = 42// meaningOfLife is inferred to be of type Int同样的,如果你没有为浮点类型的字面量指定类型,swift推测你想创建一个Double:
let pi = 3.14159
// pi is inferred to be of type Double当推测出是浮点类型的值时swift通常选择double(而不是float)。
如果你想在表达式中合并整型跟浮点型,会根据上下文推导出一个Double类型的值:
let anotherPi = 3 + 0.14159
// anotherPi is also inferred to be of type Double字面量3没有明确的类型,所以一个合适的输出类型Double是从增加的浮点类型字面量的部分推导出来的。
数字字面量(Numeric Literals)
数字字面量可以写成:
- A decimal number, with no prefix ( 十进制的数字,没有前缀)
- A binary number, with a
0bprefix (二进制的数字,前缀0b) - An octal number, with a
0oprefix (八进制的数字,前缀0o) - A hexadecimal number, with a
0xprefix (十六进制的数字,前缀0x)
这些整型字面量都有十进制的值17:
let decimalInteger = 17
let binaryInteger = 0b10001 // 17 in binary notation
let octalInteger = 0o21 // 17 in octal notation
let hexadecimalInteger = 0x11 // 17 in hexadecimal notation浮点类型字面量可以使十进制(没有前缀),或者16进制(前缀0x)。必需要在十进制小数点两边有一个数(或者16进制的数)。十六进制的浮点也可以有一个可选的指数(exponent),用大小或者小写的E指示;16禁止的浮点必需有一个exponent,使用大写或者小写的p表示。
十进制数字有一个指数exp,底数乘以10exp:
1.25e2means 1.25 x 102, or125.0.1.25e-2means 1.25 x 10-2, or0.0125.
16进制的数字有一个指数exp,底数乘以2exp:
0xFp2means 15 x 22, or60.0.0xFp-2means 15 x 2-2, or3.75.
这些浮点字面量有一个十进制的值12.1875
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0数字字面量可以包括额外的格式使他们更容易读懂。整型和浮点型都可以加额外的0,也能包含下划线来帮助解读。格式的类型都不形象字面量的真实值:
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1数字类型转换(Numeric Type Conversion)
在代码中为所有一般通用的整型常量和变量使用Int类型,即使知道它们是非负数的。每天的状况中使用默认整型类型意味着整型常量和变量在你的代码中可以立即使用并且为整型字面量匹配推导的类型。
当它们手中的任务指定需要的时候使用其他整形类型,因为外部资源的明确的尺寸数据,或者为了性能,内存使用或者其他优化的需求。这些情况下使用明确的尺寸类型有助于捕捉任何以外的值泄露和隐式的记录正在使用数据的性质
整型转换(Integer Conversion)
能储存在整型常量或者变量的数字的范围在不同的数字类型之间是不同的。一个Int8常量跟变量可以储存数字在-128跟127之间,一个UInt常量或者变量可以存储的数字在0到255之间。当编译你的代码时,一个数字不合适用某个尺寸整型类型的常量或变量时会报错。
let cannotBeNegative: UInt8 = -1
// UInt8 cannot store negative numbers, and so this will report an error
let tooBig: Int8 = Int8.max + 1
// Int8 cannot store a number larger than its maximum value,
// and so this will also report an error因为每一个数字类型能存储不同范围的值,你必须根据实际情况选择数字类型。这种选择方式防止隐藏转换错误,并且使代码中类型转换的目的更明确。
为了转换一个数字类型到另一种类型,你要使用已存在的值来初始化一个新的期望的类型的数字。下面的例子中,twoThousand常量是UInt16类型的,常量one是UInt8类型的。他们不可以直接相加,因为他们不是一个类型的。相反,这个例子调用UInt16(one)来创建一个用one的值来初始化的新的UInt16,并且使用这个值代替了原始的:
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)因为加号两边都是UInt16,加号可以执行。twoThousandAndOne输出的常量推导为UInt16类型,因为他是两个UInt16的值得和。
SomeType(ofInitialValue)是调用swift类型初始化的默认的方式,并且传入一个初始值。除此之外,UInt16有一个接受一个UInt8值得初始方法,所以这个初始方法用一个已存在的UInt8来创建一个新的UInt16.你可以传一个任意类型值,不过--一定要是UInt16为这个类型提供了初始化方法。关于已存在的类型来提供接受新类型的初始化方法的扩展在Extensions中。
整型跟浮点型转换(Integer and Floating-Point Conversion)
整型跟浮点型之间的转换必须要明确:
let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi equals 3.14159, and is inferred to be of type Double这里,常量three用来创建一个新的Double的值,所以嘉豪两边是相同类型的。没有这里的转换,加法是不允许的。
浮点类型到整型转换一定要明确。整型类型可以用一个Double或者Float的值来初始化:
let integerPi = Int(pi)
// integerPi equals 3, and is inferred to be of type Int当用这种方式来初始化一个新的整型值时浮点值通常是截取的。这意味着4.75不变成4,-3.9变成-3.
结合常量跟变量的规则跟字面量的规则是不同的。字面量3可以直接加字面量0.14159,因为字面量本身没有具体的类型,他们的类型推导只在他们被编译器求值的时候。
类型别名(Type Aliases)
类型别名为一个已经存在的类型定义另一个名称。定义别名时使用typealias关键字。
当你想通过一个更适合上下文的名字指向一个已存在的类型时类型别名很有用,例如当使用外部资源的特别的尺寸的数据时:
typealias AudioSample = UInt16一旦定义了类型别名,你可以在使用原始名称的任何地方使用别名。
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0这里,AudioSample为UInt16定义的别名,因为它是别名,调用AudioSample.min实际调用的是UInt16.min,为maxAmplitudeFound变量提供一个初始值0。
布尔值(Booleans)
swift有一个基础的Boolean类型,叫Bool。Boolean值推荐作为逻辑推理,因为他们只能是true或者false。swift提供两个Boolean常量值,true和false:
let orangesAreOrange = true
let turnipsAreDelicious = false事实上使用布尔值字面量初始化的orangesAreOrange跟turnipsAreDelicious的类型已经推导为布尔类型了。和上面Int跟Double一样,当你创建他们时就设置了true或者false的话,不需要把常量或者变量声明成Bool类型。当使用已经明确知道类型的值来初始化常量或者变量时,类型推导帮助使swift代码更简洁和可读。
当你使用条件语句像if语句时,布尔值特别好用:
if turnipsAreDelicious {
print("Mmm, tasty turnips!")
} else {
print("Eww, turnips are horrible.")
}
// Prints "Eww, turnips are horrible."像if语句的条件语句更多的细节在ControlFlow。
swift的类型安全防止非布尔值用来替代布尔值。下面的例子报了一个编译时的错:
let i = 1
if i {
// this example will not compile, and will report an error
}不过,下面另外的例子是合法的:
let i = 1
if i == 1 {
// this example will compile successfully
}i==1比较的结果是布尔值,所以第二个例子可以通过类型检查。像i==1的对比讨论在Basic Operators。
跟swift中其他类型安全的例子一样,这个方法避免意外的错误并且确保了一段代码的目的的明确性。
元祖(Tuples)
元祖将多个值打包进了一个复合的值里。元祖中的值可以是任何类型并且不需要是相同类型的。在这个例子中,(404,“Not Found”)是一个描述HTTP状态码的元祖。一个HTTP状态码是当你请求网页是网页服务器返回的特殊值。如果你的请求的网页不存在,返回的状态码是404 Not Found。
let http404Error = (404, "Not Found")
// http404Error is of type (Int, String), and equals (404, "Not Found")元组(404,“Not Found”)将Int跟String组合在一起来给HTTP状态码两个独立的值:一个数字,一个可读的描述。这个可以描述成“类型为(Int,String)的元祖”。
可以用任何排列的类型组成元祖,它们可以包含任何你想要的类型。没有任何情况阻止你有一个(Int,Int,Int),或者(String,Bool)类型,或者任何其他你需要的序列。
可以把元祖的内容分解成独立的常量或者变量,可以正常操作这些常量跟变量:
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
// Prints "The status code is 404"
print("The status message is \(statusMessage)")
// Prints "The status message is Not Found"如果你需要某一些元祖的值,当你分解元祖时使用下划线忽略元祖其他的部分:
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// Prints "The status code is 404"另外,存取元祖里单独的元素值从零开始使用下标:
print("The status code is \(http404Error.0)")
// Prints "The status code is 404"
print("The status message is \(http404Error.1)")
// Prints "The status message is Not Found"当定义元祖的时候你可以跟元祖中单独的元素命名:
let http200Status = (statusCode: 200, description: "OK")如果你给元祖中的元素命名,你可以使用元素的名字存取这些元素的值:
print("The status code is \(http200Status.statusCode)")
// Prints "The status code is 200"
print("The status message is \(http200Status.description)")
// Prints "The status message is OK"元祖在函数返回值中十分有用。尝试获取网页的函数可能返回一个(Int,String)的元祖类型来描述成功或者失败。通过返回有两个不同的值,不同类型的,函数提供了比只返回一个类型的一个值更多关于它输出的有用的信息。更多信息,查看Functions with Multiple ReturnValues。
元祖对一个相关值得分组有用。不适合创建复杂的数据结构。如果你的数据结构可能比较复杂,把它模型化类或者结构体,而不是用一个元祖。跟多信息查看Structures and Classes。
可选类型(Optionals)
当一个值可能缺失的时候使用可选类型。一个可选类型代表两个可能:有值并且可以把可选类型解包来操作值,或者根本没有这个值。
在C或者Objective-C中没有可选类型的概念。Objective-C中最相近的是可以返回nil或者返回一个对象的方法,nil意思“有效值的缺失”。不过,这只对对象有用--对结构体,基本的C类型或者枚举值不使用。对于这些类型,Objective-C方法特别反悔一个特殊值来指明的缺失(像NSNotFound)。这个方式假设方法的调用者知道有一个特殊值要再次检验并且记得检查它。swift的可选类型可以让你指示出所有类型的缺失,不必须要特殊的常量。
这里是一个可选类型如何能用来应付值的缺失的例子。swift的Int类型有一个尝试将String值转成Int类型的初始化方法。不过,不是每一个字符串可以转换成整型的。字符串“123”可以转变成数字123,但是字符串“hello,world”没有明显的值来转换。
下面的例子使用初始化方法尝试将String转换成Int:
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber is inferred to be of type "Int?", or "optional Int"因为初始化可能失败,它反悔了可选类型Int,而不是Int。可选类型Int是写作Int?,不是Int。问号指明了这个值时可选的,意味着他可能是某个Int值,或者它可能没有包含一个值。(它不能包含任何值,例如Bool值或者一个String值。它是一个Int值或者什么也不是)
nil
通过分配它一个特殊的值nil来给一个可选类型的变量一个非值状态的变量:
var serverResponseCode: Int? = 404
// serverResponseCode contains an actual Int value of 404
serverResponseCode = nil
// serverResponseCode now contains no value不能给非可选类型的常量和变量设置nil。如果代码中的常量或者变量需要在特定情况下使用空值,通常把它声明成合适类型的可选类型的值。
如果你定义了一个可选类型的变量而没有提供默认的值,变量自动设置成nil:
var surveyAnswer: String?
// surveyAnswer is automatically set to nilswift的nil和Objective-C中的nil一样。在Objective-C中,nil是一个不存在对象的指针。在swift中,nil是一个指针--一个明确类型的值的缺失。任何类型的可选类型可以设置成nil,不止对象类型。
if语句和强解包(If Statements and Forced UnWrapping)
使用if语句通过将可选类型与nil做对比来确定可选类型是否包含值。使用等于(==)或者不等于(!=)运算符进行对比。
如果可选类型有值,代表他不等于nil:
if convertedNumber != nil {
print("convertedNumber contains some integer value.")
}
// Prints "convertedNumber contains some integer value."一旦确定可选类型有值,你可以通过在可选类型名字后面添加感叹号(!)来操作它底层的值。感叹号意味着“我知道这个可选类型必定有值;可以使用”。这就是可选类型的强制解包:
if convertedNumber != nil {
print("convertedNumber has an integer value of \(convertedNumber!).")
}
// Prints "convertedNumber has an integer value of 123."关于if语句的更多信息,查看ControlFlow。
尝试使用!来操作一个不存在的可选类型会触发运行时错误。在使用!来强制解包时确定可选类型有一个非nil的值。
可选类型绑定(Optional Binding)
使用可选绑定来确定可选类型是否包含值,如果这样,让那个值可以作为临时常量或者变量来使用。可选绑定可以跟if和while语句使用来检查可选类型中的值,将那个值提取到一个常量或者变量中,作为一个单一操作的一部分。if和while语句在Control Flow中有更多详细的描述。
用if语句写可选绑定像下面这样:
if let constantName = someOptional {
statements
}可以重写Optionals章节中possibleNumber的例子来使用可选绑定而不是强制解包:
if let actualNumber = Int(possibleNumber) {
print("The string \"\(possibleNumber)\" has an integer value of \(actualNumber)")
} else {
print("The string \"\(possibleNumber)\" could not be converted to an integer")
}
// Prints "The string "123" has an integer value of 123"这段代码可以理解为:
“如果Int(possibleNumber)返回可选类型Int包含了值,设置一个新的叫做actualNumber的常量等于可选类型中包含的值”
如果转换成功了,acualNumber常量可以在if的第一个分支中使用,他已经使用可选类型中的值初始化了,所以没有必要使用!前缀来操作它的值。在这个例子中,actualNumber简单的用来打印转换的值。
可以使用常量和变量来可选类型绑定。如果你想在if语句的第一个分支中操作actualNumber的值,你可以代替使用if var actualNumber,可选类型中包含的值可以作为变量而不是常量来使用。
在if语句中可选类型绑定和布尔条件语句需要用多少就用多少,使用逗号分隔。如果可选类型的值是nil或者任何布尔条件语句是false,整个if语句的条件语句都变成了false。下面的if语句是相等的:
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
// Prints "4 < 42 < 100"
if let firstNumber = Int("4") {
if let secondNumber = Int("42") {
if firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
}
}
// Prints "4 < 42 < 100"if语句中通过可选类型创建的常量跟变量只可以在if语句的主体中使用。相反,guard语句创建的常量跟变量可以在guard语句后的代码中使用,像Early Exit中描述。
隐式解包可选类型(Implicitly Unwrapped Optionals)
就像上面描述的,可选类型标志着常量和变量可以没有值。可选类型可以使用if语句检查他有没有值,并且如果值存在的话,可以使用可选类型绑定来有条件的解包使用可选类型的值。
有时候从程序的架构中可以看出可选值通常都有值,第一次设置了值之后。在那个情况下,移除每次获取的时候的检查和解包可选类型的值会非常有用,因为可以安全的假设一直都有值。
这些可选类型的种类定义为隐式解包的可选类型。通过在你想声明为可选类型的类型后面放一个感叹号而不是问好来写一个隐式解包的可选类型。
当一个可选类型确认在可选类型第一次定义后会立即存在并且完全可以假设之后任何时候都存在时隐式解包的可选类型很有用处。swift中隐式类型的主要用途是在类初始化时,像Unowned References and Implicitly Unwrapped Optional Properties中描述的。
背后隐式可选类型是一个普通的可选类型,但是也可以像非可选类型一样使用,不需要每次使用的时候都解包。下面的例子展示了当使用可选字符串类型与隐式可选字符串类型的解包值时他们之间表现的不同。
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation mark
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // no need for an exclamation mark可以把隐式解包可选类型当做使可选类型每次使用时自动解包。而不是每次用它时都在可选类型名称后面加一个感叹号,当你声明可选类型时,在他的类型后面加一个感叹号。
如果隐式解包可选类型是nil,并且你尝试操作它的解包的值,将会触发运行时错误。结果跟你在普通可选类型后面加感叹号而它没有值时一样。你可以把隐式可选类型当做一个正常的可选类型,检查他是否含有值。
if assumedString != nil {
print(assumedString!)
}
// Prints "An implicitly unwrapped optional string."你也可以用可选绑定来使用隐式解包可选类型,来在句语句中检查并解包它的值:
if let definiteString = assumedString {
print(definiteString)
}
// Prints "An implicitly unwrapped optional string."当在后面变量可能变成nil时不要使用强制解包。如果在变量生命周期中你要检查可选类型是否是nil,通常使用普通可选类型。
错误处理(Error Handling)
使用error handling响应执行期间程序可能崩溃的错误情况。
和可选类型不同,可以使用一个值得拥有或者缺失来来显示函数的成功或者失败,error handling允许你决定失败的背后原因,并且,如果有必要,将错误传递给程序的其他部分。
当函数遇到了错误的情况,它会抛出错误。函数调用者可以catch错误,并且做出合适的响应。
func canThrowAnError() throws {
// this function may or may not throw an error
}通过在函数的声明中包含一个throws关键字来指明它可以抛出一个错误。当你调用可能抛出错误的函数时,你在表达式前面标记try关键字。
直到函数自己通过catch从句处理了错误,swift自动将错误从当前作用域中传递出错误。
do {
try canThrowAnError()
// no error was thrown
} catch {
// an error was thrown
}一个都语句创建了一个新的控制区域,允许错误传递给一个或多个catch从句。
这里的例子展示了error handling如何用来响应不同的错误情况:
func makeASandwich() throws {
// ...
}
do {
try makeASandwich()
eatASandwich()
} catch SandwichError.outOfCleanDishes {
washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
buyGroceries(ingredients)
}这个例子中,makeASandwich()函数将会抛出错误,如果没有干净的盘子可用或者如果原料小时了。因为makeASandwich()可以抛出错误,调用函数打包在了try表达式。通过将函数调用打包在do语句中,任何将要抛出的错误将会传到所提供的catch从句中。
如果没有错误抛出,eatASandwich()函数将会调用。如果有一个错误抛出了并且它匹配了SandwichError.outOfCleanDished事例,那么wishDishes()函数将会调用,然后bugGroceries(_:)函数将会和由catch模型捕捉到的相关联的[String]类型的值一起调用。
抛出,捕捉,传递错误的更详细的内容在Error Handling中。
断言和前提(Assertions and Preconditions)
断言跟前提在运行期检查。在执行更多的代码之前使用它们确保满足了基本的情况。如果断言跟前提中的布尔条件计算是true,代码将会和平常一样运行。如果条件计算是false,程序当前的状态是不合法的;代码执行结束,APP终止。
使用断言和前提表达你做的假设并且编码的时候的期望,所以可以把它们作为代码的一部分。断言帮你在开发阶段发现错误和错误的假定,并且前提帮你检测生产阶段的问题。
除了验证运行时期的期望,断言和前提在代码中的注释变得很有用。不像上面Error Handling中错误状态的描述,断言和前提不能用来获得或者预料错误。因为错误的断言和前提标志了不合法的编程状况,没有办法获取失败的断言。
使用断言和前提不能用来替代以非法状态可能出现的方式设计代码。不过,使用它们强化合法数据和状态可以让你的APP在可能出现非法状态时更直接的终止,让问题更简单的来调试。非法状态出现时立即停止运行可以限制非法状态造成的危害。
断言和前提的区别是他们什么时候检测:断言只在debug建立时检测,但是前提在debug跟production建立时都检测。在production建立时,断言中的状态是不会执行的。这意味着在开发阶段你可以随意使用断言,不会影响production的表现。
使用断言调试(Debugging with Assertions)
通过调用swift标准库中的assert(_:_:file:line:)函数来写一个断言。传给函数一个计算true或者false的表达式和一个如果条件结果是false时展示的信息。例如:
let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// This assertion fails because -3 is not >= 0.这个例子中,如果age>=0计算是true代码继续执行,也就是,如果age的值时非负的。如果age的值是负数,像上面的代码,那么age>=0计算得false,断言失败,程序终止。
你可以忽略断言信息--例如,值时简单的重复状态。
assert(age >= 0)如果代码已经检查了状态,可以使用assertionFailure(_:file:line:)函数来指明断言结果是错误的。例如:
if age > 10 {
print("You can ride the roller-coaster or the ferris wheel.")
} else if age >= 0 {
print("You can ride the ferris wheel.")
} else {
assertionFailure("A person's age can't be less than zero.")
}强制前提(Enforcing Preconditions)
状态任何时候都可能是错的时候使用前提,但是一定要是true你的代码才会继续执行。例如,用前提检测下标越界了,或者检查函数传入了合法的值。
通过调用precondition(_:_:file:line:)函数写一个前提。传给这个函数一个计算为true或者false的表达式和一个如果状态是false时展示的信息。例如:
// In the implementation of a subscript...
precondition(index > 0, "Index must be greater than zero.")你也可以调用preconditionFailure(_:file:line:)函数来指明错误已经发生了--例如,如果switch的默认事例发生了,但是所有合法的输入的数据应该被switch其他事例中的一个来处理。
如果编译在不检测模式(-Ounchecked),前提不会检测。编译器假设前提总是true,会直接优化你的代码。不过,函数fatalError(_:file:line:)一直会中断执行,不过优化设置。可以在原型跟开发前期使用fatalError(_:file:line:)函数为还没有实现的函数创建存根,通过把实现的存根写作fatalError("Unimplemented"),你可以确保如果遇到了实现的存根执行总是会中断。