Swift2学习:语言指南-基础部分

基础部分

Swift是IOS,OS X和watchOS上应用开发的一门新语言。尽管如此,如果你有C或者Object-C可发经验的话,你会发现Swift的很多内容都很熟悉。

Swift提供的所有类型都是基于C和Object-C的基础上,包括,Int代表整形,Double和Float代表浮点型,Bool代表布尔型,String代表文本数据。Swift同样提供了强大的三个基本集合类型,Array,Set和Dictionary,详见集合类型。

就像C,Swift使用变量来存储和引用值并通过变量关联值。值不可变的变量在Swift中也有着广泛的应用。他们是常量,并且比C中的常量更加强大。当你处理的值不需要改变时,Swift中使用常量可以确保代码的安全和整洁。

除了熟悉的类型,Swift引入了高级类型,当然在Object-C中找不到,像元组。元组使得你能够创建并传入一组数据。你可以使用元组从一个函数返回多个值。

Swif也引入了可选类型,用于处理值确实的情况。可选大声吼着“那里有个值,它等于x”或者吼着“那里一个值都没有”。可选有点像在Object-C中使用空指针,但是他可以用在任何类型上,不仅仅是类。可选类型比Object-C中的空指针更安全更有表现力,并且他是Swift中很多强大特征的核心。

Swift是一门类型安全语言,可选就是一个例证。Swift帮助你清楚的知道你的代码中值的类型。类型安全会通过一个错误来阻止你传入一个Int。这个措施确保你在开发过程中尽早的捕获并修正错误。

常量和变量

常量和变量把一个名字(就像maximumNumberOfLoginAttempts或者welcomeMessage)和一个指定类型的值(就像整数10或者字符串“Hello”)。常量的值一经赋值不可改变,然而变量想怎么改都行。

声明常量和变量

常量和变量必须在使用前声明。用let关键字声明常量,用var关键字声明变量。下面的例子记录了怎样用常量和变量记录用户尝试登录的次数

let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0

这两行代码可以理解为:

“声明一个新的叫做maximumNumberOfLoginAttempts的常量,并且给他赋值10.然后,声明一个新的叫做currentLoginAttempt的变量,并且给他初始化值为0.”

在这个例子中,登录最大尝试次数被声明为一个常量,因为这个最大值不会改变。当前尝试次数被声明为一个变量,因为这个值在每回登录失败后会增加。

你可以尽在用一行声明一组常量或是变量,用逗号分隔:

var x = 0.0, y = 0.0, z = 0.0

注意

如果你的代码中又不需要改变的值,用let声明他。变量尽用来存储需要改变的值。

类型标注

当你声明一个常量或是变量时你可以提供一个类型标注(type annotation),来说明常量或是变量将要存储的值的类型。在常量或变量的名称后面放置一个冒号,紧跟着来一个空格,最后写上要使用的类型名称,这样就是一个类型标注。

下面的例子为一个叫welcomeMessage的变量提供了类型标注,来指出变量存储字符串类型的值:

var welcomeMessage: String

冒号在声明语句中意味着“是...类型(..of type...)”,所以这行代码可以理解为:

“声明一个叫welcomeMessage的变量,他的类型是String。”

类似为String的意思是可以存储String类型的值。想象他是被存储的东西的类型(或者东西的种类)。

现在可以给welcomeMessage设置任意字符串类型的值而不用担心报错:

welcomeMessage = "Hello"

你可以只用一行定义一组相关联相同类型的变量,通过逗号分割,在最后一个变量后面加上类型标注:

var red, green, blue: Double

注意

在实际情况中你很少会用到类型标注。如果你为常量或变量提供了一个初始值,此时类型已经确定,Swift可以推断出这个常量或变量的类型,参见类型安全与类型推断。在上面welcomeMessage例子中,welcomeMessage没有初始值,所以welcomeMessage的类型通过类型标注指定,而不是从初始值推断。

变量和常量的命名

变量和常量的命名几乎可以包括任何字符,包括Unicode字符:

let π = 3.14159
let 你好 = "你好世界"
let "这是表情" = "dogcow"

常量和变量的命名不能包含空格,数学符号,箭头,保留的(或非法的)Unicode码位,连线和制表符。也不能以数字开头,当然数字可以包含在命名的其他地方。

一旦你将常量或变量声明为确定的类型,你不能用同样的名字再次声明他,或者改变他存储值的类型。也不能把常量变成变量,或者把变量改为常量。

注意

如果你需要用Swift的保留字相同的名称为常量或变量命名,当你使用他时用反引号包围他。然而,尽可能避免用保留字命名除非你别无选择。

var `let` = "let"
print(`let`)

你可以用相同类型的值改变现有变量的值。在下面的例子中,friendlyWelcome的值从"Hello!"变为"Bonjour!":

var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
//friendlyWelcome is now "Bonjour!"

与变量不同,常量的值一经设置不可改变。尝试这么做代码编译时会报一个错误:

let languageName = "Swift"
languageName = "Swift++"
// this is a comple-time error - languageName cannot be changen

打印常量和变量

你可以用print(_:)函数打印常量或变量当前的值:

print(friendlyWelcome)
//prints "Bonjour!"

print(_:)是用于打印的全局函数,输出后会换行。在Xcode中,print(_:)函数会把内容打印在“console”面板中(控制台)。(一个相近的函数print(_:appendNewline:),做同样的事可选参数来标识在输出末尾是否换行。)

print(_:)函数打印任何传入的String值:
print("This is a string")
// prints "This is a string"

print(_:)函数可以打印更加复杂的日志信息。这些信息包括变量和常量当前的值。

Swift使用字符串插值(string interpolation)把常量和变量做为占位符插到长字符串中,Swift会迅速的用变量或常量当前的值替换他。将变量或常量名放入圆括号中,在开括号之前用反斜杠进行转义。

print("The current value of friendlyWelcome is \(friendlyWelcome)")
// prints "The current value of friendlyWelcome is Bonjour!"

注意

字符串插值(string interpolation)的所有操作,请参考字符串插值(string interpolation)章节。

注释

将你的代码中不可执行文本注释,做为笔记或提示方便阅读。当你的代码被编译时,注释会被Swift编译器忽略。

Swift中的注释和C中的注释很像。单行注释以双斜杠(//)开头:

// this is a comment

多行注释以一个单斜杠接着一个星号(/*)开始,以星号接着一个单斜杠(*/)结束:

/* this is also a comment,
but 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*/

通过嵌套多行注释,你可以快速方便的注释掉一大段代码,即使这段代码中已经包含多行注释。

分号

不像其他许多语言,Swift不要求你在每行语句的末尾写分号(;),当然,如果你想这么做也没问题。然而,如果你想在一行写多条独立语句,分号必须有:

let cat = "mao"; print(cat)
// prints "mao"


整数

整数是没有小数部分的数字,像42和-23.整数可以是有符号的(正,零,负)或者无符号的(正,零)。

Swift提供了8,16,32和64位的有符号和无符号整形。这些整形的命名方式和C很像,比如8位无符号整形是UInt8,32位有符号整形是Int32.就像Swift中其他类型,整形采用大写命名方式。

整数范围

你可以用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,与当前平台的原生字节的长度相同。

    ·在32为平台上,Int的长度与Int32相同。

    ·在64位平台上,Int的长度与Int64相同。

除非你需要处理特定长度的整形,否则在你的代码中使用Int做为整形值。这回帮助你提高代码的一致性和复用性。即使在32为平台上,Int可以存储-2,147,483,648到2,147,483,648的值,他已经足够大应付大多数情况。

UInt

Swift也提供了一个无符号整形,UInt,并且与当前平台的原生字节长度相同:

    ·在32为平台上,Int的长度与UInt32相同。

    ·在64位平台上,Int的长度与UInt64相同。

注意

只有当你明确的需要一个与当前平台原生字节相等的无符号整形,使用UInt。如果不是这种情况,Int是首选,甚至已经知道存储的值是正的。统一使用Int可以提供代码的一致性,避免整形类型之间的转换,并匹配数字类型推测,详见类型安全与类型推测

浮点数

Floating-point numbers是有小数部分的数字,像3.14159,0.1和-273.15.

浮点数比整形能表示更宽广的值范围,并且所存储的数字比整形更大或更小。Swift提供了两种有符号浮点数类型:

    ·Double代表一个64位的浮点数。

    ·float代表一个32位的浮点数。

注意

Double精确到小数点后15位,然而Float只精确到小数点后6位。使用哪个的浮点类型取决于在你的代码中需要处理的自然值的范围。在两种情况都合适的情况下,推荐使用Double。

类型安全与类型推测

Swift是一门类型安全语言。类型安全语言鼓励你清楚的指导代码中处理的值的类型。如果你代码的某部分希望是一个字符串,你不可能不小心给他传个整形。

因为Swift是类型安全的,所以他在代码编译时执行了类型安全检查并把不匹配的类型做为错误标注出来。这可以使你在开发过程中尽早的捕获并修正错误。

当你处理不同类型的值时类型检查帮助你避免发生错误。然而,这并不意味着你必须显式指明你所声明的常量或变量的类型。如果你没有指定你需要值的类型,Swift使用类型推测(type inference)来选择适合的类型。类型推测使得编译器在编译代码时自动推断出表达式的类型,仅仅通过测试你赋的值。

辛亏有了类型推测,与C或者Object-C比较Swift需要更少的类型声明。常量和变量虽然需要明确类型,但大部分确定类型的工作已经为你做好了。

当你初始化一个常量或变量时类型推测特别有用。当你在声明一个常量或变量赋给它一个字面量(literal value)就会触发类型推测。(字面量是直接出现在你代码中的值,像,在下面的例子中的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 alse inferred to be of type Double

字面值3没有显式声明类型,在条件中出现了一个浮点类型的字面值,所依Double是最合适的输出类型。

数值型字面量

整形的字面量可以被写成:

    ·一个十进制数,没有前缀

    ·一个二进制数,前缀0b

    ·一个八进制数,前缀0o

    ·一个十六进制数,前缀0x

所有一下的整形字面量都有一个十进制的值17:

let decimalInteger = 17
let binaryInteger = 0b10001      // 17 in binary notation
let octalInteger = 0o21          // 17 in octal notation
let hexadecimalInteger = 0x11    // 17 in hexadeciaml

浮点数的字面量可以是十进制(没有前缀),或者十六进制(前缀0x)。在小数点两边至少要用一个数字(或16进制数)。浮点字面量还有一个可选项指数,十进制数通过大写或小写的e来制定,十六进制通话大写或小写的p指定。

如果一个十进制数的指数是exp,那么这个数相当于基数乘以10^exp:   

   ·1.25e2等于1.25 * 10^2或125.0

   ·1.25e-2等于1.25 * 10^-2或0.0125.0

如果一个十六进制数的指数是exp,那么这个数相当于基数乘以2^exp:

   ·0xFp2等于15 * 2^2或60.0

   ·0xFp-2等于15 * 2^-2或3.75

所有下面的浮点数字面量都有一个十进制值12.1875:

let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0

数值型字面量可以包含额外的格式使他们更易于阅读。整数和浮点数都可以增加额外的零和下划线来帮助阅读。没有一个格式化会影响字面量的值:

let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1


数值型类型转换

在通常情况下,在你的代码中使用Int类型,甚至已经知道他们是无符号整形。在任何情况下使用默认整数类型意味着整形常量和变量可以直接复用,并在你的代码中匹配整形字面量类型推测。

使用其他整数类型,仅仅当他们被手头上任务明确需要时(从明确大小的外部源数据,或为了性能,内存使用,或其他必须的操作)。在这些情况下使用明确大小的类型帮助你捕获任何值溢出并且暗示记录正在使用数据的性质。

整数转换

一个整形常量或变量所能存储的数字的范围在不同的数字类型之间是不同的。一个Int8类型的常量或变量能存储-128到127的数字,然而,一个UInt8类型的常量或变量能存储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)来创建一个UInt16的数字并用one值初始化,再用这个值代替就值参加计算:

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提供构造器类型的值。你可以扩展现有的类型来接收新类型的值(包括自定义类型),详见扩展

整数和浮点数转换

整数和浮点数之间的类型转换必须显示进行:

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直接相加,因为字面量本身没有显示的类型。他们的类型通过编译器在运行的时候被推测出来。


类型别名

类型别名为已存在的类型提供另外一个名字。用typealias来定义类型别名。

当你想给现有的类型起一个更有意义的名字时,类型别名非常有用,比如当你在处理制度长度的外部源数据时:

typealias AudioSample = UInt16

一旦你定义一个类型别名,你可以在任何使用原始类型名的地方使用别名:

var maxAmplitudeFoune = AudioSample.min
// maxAmplitudeFoune is now 0

这回,AudioSample被定义为UInt16的别名。由于他是别名,AudioSample.min调用实际上是在调用UInt16.min,为变量maxAmplitudeFoune提供了一个0的初始化值。


布尔值

Swift有一个基本的布尔类型,叫Bool。布尔值是逻辑上的,因为他只能是真或假。Swift有两个布尔常量,true和false:

let orangesAreOrange = true
let turnipsAreDelicious = false

orangesAreOrange和turnipsAreDelicious类型被推测为布尔类型,很显然他们用布尔字面量来初始化。正如之前的Int和Double一样,如果你创建他们用true或false赋值,那你不必声明常量或变量的类型是Bool。用已知类型初始化常量或变量时,类型推测被触发,这帮助Swift代码更加简洁易读。

布尔值在你处理条件语句是非常有用,就像if语句:

if turnipsAreDelicious {
    print("Mum, tasty turnips!")
} else {
    print("Eww, turnios are horrible.")
}
// prints "Eww, turnios are horrible."

条件语句,例如if,在控制流中有详细的介绍。

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的比较结果是Bool类型,所以第二个例子通过类型检查。像i == 1这样的比较会在基本操作符中讨论。

和Swift中其他类型安全的例子一样,这个方法避免了意外的错误并保证这块代码的意图总是清晰的。


元组

元组把多个值组成一个复合值。元组各元素的值可以是不同类型,并不要求他们类型相同。

在这个例子中,(404, "Not Found")是一个结束HTTP状态码的元组。HTTP状态码是每当你请求一个web页时,web服务器返回的一个特殊值。404 Not Found状态码是当你请求一个不存在的web页时的返回值。

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"

此外,你还可以用从0开始的下标来访问元组中元素的值:

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, statusMessage: "OK")

如果你命名了元组中的元素,你就可以用元素名来访问元素的值:

print("The status code is \(http200Status.statusCode)")
// prints "The status code is 200"
print("The status message is \(http200Status.statusMessage)")
// prints "The status message is OK"

元组在函数中需要返回多个值时非常有用。一个尝试获取网页的函数可能会返回一个(Int, String)类型的元组来描述是否获取成功。和只返回一个类型的值比较起来,一个包含两个不同类型的值的元组可以让函数的返回信息更有用。详见多个返回值的函数

注意

元组对于临时组织值的时候很有用。他们不适合创建复杂的数据结构。如果你的数据结构要长期使用,采用类或结构体而不是元组。详见类和结构体


可选

使用可选来处理值缺失的情况。可选表示:

    ·这里有个值,他等于x

或者

    ·这里什么值都没有

注意

在C或Object-C中没有可选概念。最相近的是Object-C中的一个特性,一个方法要么返回nil要么返回一个对象,nil的意思是“缺少一个合法的对象”。然而,这只对对象起作用-对结构体,基础C类型或枚举值无效。对这些类型,Object-C方法一般会返回一个特殊的值(像NSNotFound)来指出值的缺失。这个方法假定方法的调用者知道这里有个特殊值要判断并记得检查他。Swift的可选让你指出任意类型值的缺失,并且不需要一个特殊的常量。

这里有一个怎样使用可选来处理值缺失的例子。Swift的String类型有一个尝试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"

因为可能构造失败,所以他返回一个可选的(optional)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 antomatically set to nil

注意

Swift的nil和Object-C中的nil不同,在Object-C中,nil是一个指向不存在对象的指针。在Swift中,nil不是指针--他是确定类型值的缺失。任何类型的可选都可以设置成nil,不仅仅是对象类型。

if语句和强制解析

你可以使用if语句通过与nil比较来判断一个可选是否包含值。你通过”等于“(==)或者”不等“(!=)执行比较操作.

如果一个可选有值,他被认为不等于nil:

if convertedNumber != nil {
    print("convertedNumber contains some integer value.")
}
// prints "convertedNumber contains some integer value."

一旦你确定可选包含一个值,你可以通过可选名字后面加一个感叹号(!)来访问他的值。感叹号有力的说,”我知道这个可选有值;请使用他。“这被称为可选值的强制解析(forced unwraping):

if convertedNumber != nil {
    print("convertedNumber has an integer value of \(convertedNumber) .")
}
// prints "convertedNumber has an integer value of 123."

想更多了解if语句,见控制流

注意

使用!来获取一个不存在的可选值会触发一个运行时错误。使用!强制解析之前,一定要确保可选包含一个非nil的值。

可选绑定

你可以使用可选绑定(optional binding)来找出可选是否包含一个值,如果包含就把这个值赋给一个临时的常量或变量。可选绑定可以与if和while语句一起使用来检查可选是否有值,并取出改值赋给一个常量或变量,做为一个单独的章节,if和while语句请详见控制流

想下面样子写一个可选绑定:

if let constantName = someOptional {
    statements
}

你可以把可选章节的possibleNumber例子重写,并使用可选绑定而不是强制解析:

if let actualNumber = Int(possibleNumber) {
    print("\'\(possibleNumber)\' has an integer value of \(actualNumber)")
} else {
    print("\'\(possibleNumber)\' coule not be converted to an integer")
}
// prints "'123' has an integer value of 123"

这段代码可以理解为:

“如果Int(possibleNumber)返回的可选Int包含一个值,声明一个新的叫actualNumber的常量并把可选包含的值赋给他。”

如果转换成功,常量actualNumber可在if分支内使用。他已经用可选包含的值初始化,所以这里必须有用!后缀来取值。在这个例子中,actualNumber只是用来打印转换的结果。

你可以在常量或变量中使用可选绑定。如果你想在if语句的第一个分支中操作actualNumber的值,你应该用if var actualNumber替换,这样可选中包含的值作为一个变量使用而不是常量。

若干可选绑定可以同时出现在一个if语句中用作为一个逗号分割的赋值语句列表。

if let constantName = someOptional, anotherConstantName =someOtherOptional {

    statements

}

隐式解析可选

像上面介绍的那样,可选指明了一个常量或变量允许有“没有值”的值。可选可用if语句检查是否有值存在,也可以用可选绑定有条件的解析,如果存在的话。

有时从程序的结构上可以发现一个可选在赋值之后总是有值。在这些情况下,移除检查部分直接解析是非常高效的,因为每次都可以安全的取到需要的值。

这种可选被定义为隐式解析可选(implicitly unwrapped optionals)。要在你想要可选类型的后面通过一个感叹号标识(String!来声明一个隐式解析可选而不是一个问号标识(String?)。

当可选值第一次定义并确定一直有值时,隐式解析可选非常有用。在Swift中隐式解析可选主要的用法是在初始化类时,详见弱引用和隐式解析可选属性。

一个隐式解析可选其实是一个普通的可选,但是也可被当做非可选使用,不需要在每次使用时解析可选的值。接下来的例子展示了,当访问要解析的值是String时,一个字符串可选字符串和一个隐式解析可选字符串之间不同的行为:

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

你可以把隐式解析可选当做自动解析可选。你要做的是声明是在可选类型的后面加一个感叹号标识,而不是每次使用时在可选名称后面加个感叹号。

注意

如果你试图访问一个没有值的隐式解析可选,你会触发一个运行时错误。这个结果实际上和你在一个不包含值的普通可选后面加上一个感叹号一样。

你仍然可以把一个隐式解析可选当做一个普通的可选来检查他是否包含值:

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判断,一定要使用普通的可选。


错误处理

你可以使用错误处理在你的程序运行期间返回一个错误。

与可选对照,错误处理可以存在或不存在或用一个值实时表示一个函数成功或失败,错误处理允许你对导致潜在失败的原因做出决定,并且,如果必要的话,把错误传递到你代码的另外部分。

当一个函数遇到一个错误时,他抛出一个错误。函数的调用者就可以捕获这个错误并做出恰当的回应。

func canThrowAnError() throws {
    // this function  may of may not throw an error
}

一个函数要通过在他的声明中包含throws关键字来表明他可以抛出一个错误。当你调用一个可以抛出错误的函数时,你要为表达式提前考虑try关键字。

Swift会自动从当前区域传递出错误信息直到他被一个catch分支处理。

do {
    try canThrowAnError()
    // no error was thrown
} catch {
    // an error was thrown
}

一个do语句创建了一个新的块范围,并允许吧错误传递到一个或多个catch分支。

这里有一个在不同等错误情况下怎样使用错误处理进行回应:

func makeASandwich() throws {
    // ...
}

do {
    try makeASandwich()
    eatASandwich()
} catch Error.OutOfCleanDishes {
    washDishes()
} catch Error.MissingIngredients(let ingredients) {
    buyGroceries(ingredients)
}

这这个例子中,makeASandwich()函数如果没有干净的盘子可用或如果某些原料缺失就会抛出一个错误。因为makeASandwich()可以抛出异常,该函数的调用被封装在try表达式中。通过把该函数调用封装在do语句中,任何被抛出的错误都会传递到已经提供的catch分支中。

如果没有错误抛出,eatASandwich()会被调用。如果一个错误被抛出并且并匹配Error.OutOfCleanDishes分支,那么washDishes()函数会被调用。如果抛出的错误匹配Error.MissingIngredients分支,那么buyGroceries(_:)函数会被调用,并传入通过catch模式捕获的[String]值。

抛出,捕获,和传递错误在错误处理章节中有非常详细的阐述。


断言

在某些情况下,如果一个特定的条件没有满足你的代码坦白说不可能执行。在这些情况下,你可以在你的代码中触发一个断言(asssertion)来终止代码运行,并提供一个机会来调试值确实或无效值的原因。

使用断言调试

一个断言就是运行时检查一个逻辑条件是否为true。从字面意思上来说,断言“断言”是否为真。你使用断言来确定在运行其他代码之前是否满足必要条件。如果条件是true,代码会继续执行;如果条件是false,代码终止执行,并且你的应用被终止。

如果你的代码在调试环境运行时触发断言,就像你在Xcode中创建并运行一个应用,你可以清楚的看到不合法的状态发送并检查你的应用在触发断言时的状态。断言也允许你提供一条合适的调试信息做为断言的属性。

你要通过调用assert(_:_:)函数来写一个断言。你要给assert(_:_:)函数传递一个等于true或者false的表达式和一条如果条件为false展示的信息:

let age = -3
assert(age >= 0, "A person's age cannot be less than zero")
// this causes the assertion to trigger, because age is not >= 0

在这个例子中,代码会在age >= 0为真的情况下继续运行,也就是说,当age的值非负时。如果age的值是负数,就像上面的代码,age >= 0结果为false,断言被触发,结束应用。

断言信息可以省略,就像下面的例子:

assert(age >= 0)

何时使用断言

当一个条件可能是false时,使用断言,但是为了你的代码能继续运行,最终要保证条件为true。以下是断言适用的情景:

    ·一个整数下标索引被直接传入一个自定义脚本,但是下标索引值可能太低或太高。

    ·一个值被传入一个函数,但是一个无效的值意味着该函数无法完成他的任务,

    ·一个可选值当前为nil,但是要使后面的代码运行成功,一个非nil值是必要的。

同见脚本和函数

注意

断言会导致你的app终止运行,并且不能替代好的代码设计。尽管如此,在你的应用发布之前,无效条件可能出现的情况下,断言是一个有效的方法来保证开发过程中一些条件被醒目的标注出来。

你可能感兴趣的:(Swift2学习:语言指南-基础部分)