Swift5.2-基础(中文文档)

引言

继续学习Swift文档,从上一章节:开篇 ,咱们了解Swift基本的知识点,如今咱们仍是从详细的基础知识开始,不要认为基础知识不重要,这是掌握一门开发语言的基本。因为篇幅较长,这里分篇来记录,接下来,开始吧!html

若是你已经掌握了Swift的基础,那么请参阅下一章节:基本操做web

基础

Swift是一门新的针对iOS、macOS、watchOS和tvOS开发的编程语言。尽管如此,根据您使用C和Objective-C进行开发的经验,您将熟悉Swift的许多部分。编程

Swift提供了本身版本的全部基本C和Objective-C类型,包括Int对应整型,Double和Float对应浮点类型,Bool对应布尔类型,和String对应文本数据。Swift还提供了三种主要收藏类型的强大版本,Array,Set和Dictionary,定义在 Collection Types.swift

像C同样,Swift使用变量来存储和经过一个标识名称引用值。Swift还大量使用了不能更改值的变量。这些被称为常量,比c中的常量功能强大得多。当你处理不须要更改的值时,在Swift中使用常量使代码更安全、更清晰。安全

除了熟悉的类型,Swift还引入了Objective-C中没有的高级类型,好比元组。元组使您可以建立和传递值分组。可使用元组将函数中的多个值做为一个复合值返回。bash

Swift还引入了可选类型,用于处理缺乏值的状况。可选值要么说“有一个值,它等于x”,要么说“根本没有值”。使用可选值相似于在Objective-C中对指针使用nil,但它们适用于任何类型,而不只仅是类。option不只比Objective-C中的nil指针更安全、更有表现力,并且是Swift许多最强大功能的核心。服务器

Swift是一种类型安全的语言,这意味着该语言能够帮助您清楚地了解代码可使用的值的类型。若是您的部分代码须要String,类型安全防止您错误地将其传递为Int。一样,类型安全能够防止您意外地将可选String传递给须要非可选String的代码段。类型安全能够帮助您尽早捕获和修复开发过程当中的错误。数据结构

1 常量和变量

常量和变量将名称(如maximumnumberoflogintries或welcomeMessage)与特定类型的值(如数字10或字符串“Hello”)关联起来。常量的值一旦设置就不能更改,而变量能够在未来设置为不一样的值。app

1.1 定义常量和变量

常量额变量在使用前必须声明。能够用let关键字声明常量,用var关键字声明变量。下面是一个如何使用常量和变量来跟踪用户尝试登陆的次数的例子:less

let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
复制代码

这段代码意思是: “定义一个maximumNumberOfLoginAttempts常量,并赋值为10。定义一个currentLoginAttempt变量,并赋值为0.”

在这个例子中,声明了一个最大容许登陆次数的常量,由于这个最大值没有被修改。当前登陆尝试计数器被声明为一个变量,由于在每次登陆尝试失败后,该值必须递增。

你能够在一行里声明多个常量和多个变量,用逗号分隔:

var x = 0.0, y = 0.0, z = 0.0
复制代码

注意 若是代码中存储的值不会改变,请始终使用let关键字将其声明为常量。变量只用于存储须要更改的值。

1.2 类型注解

当你声明常量和变量时,能够添加类型,这样就能够知道须要存储的值的类型。经过在常量或变量名后面加上冒号、空格和要使用的类型名来编写类型注释。

这个示例为一个名为welcomeMessage的变量提供了一个类型注释,以代表该变量能够存储字符串值:

var welcomeMessage: String
复制代码

声明中的冒号表示“…of type…”,所以上面的代码能够理解为:

“定义一个名称叫welcomeMessage的字符串变量。”

短语“of type String”意味着“能够存储任何字符串值”。把它看做是能够存储的“事物的类型”(或“事物的种类”)的意思。

welcomeMessage变量能够设置为任何字符串值:

welcomeMessage = "Hello"
复制代码

你能够在一行中定义多个相同类型的相关变量,用逗号分隔,最后的变量名后面有一个类型注释:

var red, green, blue: Double
复制代码

实际上,不多须要编写类型注释。若是在定义常量或变量时提供初始值,Swift几乎老是能够推断该常量或变量使用的类型,如 类型安全和类型推断 所述。在上面的welcomeMessage示例中,没有提供初始值,所以welcomeMessage变量的类型是经过类型注释指定的,而不是从初始值推断出来的。

1.3 命名常量和变量

常量和变量的名称支持几乎全部字符,包括Unicode字符:

let π = 3.14159
let 你好 = "你好世界"
let 🐶🐮 = "dogcow"
复制代码

常量个变量的名称不能包含空格数学符号箭头私有Unicode标量值,或**-box-drawing**字符。也不能以数字开头,尽管数字能够包含在名称的其余地方。

一旦声明了一个某些类型的常量和变量,就不能声明和它相同名称的,或者改变它存储的类型。尽管能够将一个常量修改成变量或者一个变量修改成常量。

注意 若是您须要为一个常量或变量提供与保留的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.
复制代码

1.4 打印常量和变量

能够经过**print(_:separator:terminator:)**函数打印常量或变量:

print(friendlyWelcome)
// Prints "Bonjour!"
复制代码

**print(:separator:terminator:)**函数是一个全局函数,它将在适当的输出中打印一个或多个值。在Xcode中,例如,**print(:separator:terminator:)**函数会在Xcode的控制台上输出打印的值。 separator和 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都有描述。

2 注释

使用注释将不可执行文本包含在代码中,做为对本身的提示或提醒。代码编译时,Swift编译器会忽略注释。

Swift中的注释和c的很类似。单行注释用双斜杠(//):

// This is a comment.
复制代码

多行只是能够用/* ... */:

/* This is also a comment
but 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. */
复制代码

嵌套多行注释使您可以快速而轻松地注释掉大块代码,即便代码已经包含多行注释。

3 分号

和许多其余语言不一样,Swift不须要在语句末尾写分号,尽管你但愿这样作。然而,当将多个语句写在同一行时,须要加分号分隔:

let cat = "🐱"; print(cat)
// Prints "🐱"
复制代码

4 整型

整数是没有分数成分的整数,例如42和-23。整数能够是有符号的(正、零或负)或无符号的(正或零)。

Swift提供8位、16位、32位和64位格式的有符号整数和无符号整数。这些整数遵循相似于C的命名约定,即8位无符号整数的类型为UInt8, 32位有符号整数的类型为Int32。与Swift中的全部类型同样,这些整数类型的名称都是大写的。

4.1 整数范围

您能够访问每一个整数类型的最小值和最大值及其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),所以能够在表达式中与其余相同类型的值一块儿使用。

4.2 Int

在大多数状况下,您不须要在代码中选择特定大小的整数。Swift提供了一个额外的整数类型Int,它与当前平台的本地字大小相同:

  • 在32位平台上,Int大小和Int32相同。
  • 在64位平台上,Int大小和Int64相同。

除非你须要处理一个特定大小的整数,不然整数都要用Int定义。这有助于代码的一致性和互操做性。即便在32位平台上,Int能够存储的值在-2,147,483,648和2,147,483,647之间,这个范围能够用在大多数整数上了。

4.3 UInt

Swift也提供了无符号的整型,UInt,它与当前平台的本地字大小相同:

  • 在32位平台上,UInt大小和UInt32相同。
  • 在64位平台上,UInt大小和UInt64相同。

注意 仅当您特别须要与平台的本地字大小相同的无符号整数类型时,才使用UInt。若是不是这样,则首选Int,即便已知要存储的值是非负的。整数值一致地使用Int有助于代码的互操做性,避免不一样数字类型之间的转换,并匹配整型类型推断,如类型安全和类型推断中所述。

5 Floating-浮点数

浮点数是由小数部分组成的数,如3.1415九、0.1和-273.15。

与整型相比,浮点型能够表明更大范围的值,而且能够存储比整型更大或更小的数字。Swift提供了两种带符号的浮点数类型:

  • Double表示一个64位浮点数。
  • Float表示一个32位浮点数。

注意 Double的精度至少为15位十进制数字,而Float的精度只有6位十进制数字。要使用的适当浮点类型取决于代码中须要使用的值的性质和范围。若是两种类型都合适,则首选Double。

6 类型安全和类型推断

Swift是一种类型安全的语言,类型安全语言鼓励您明确代码可使用的值的类型。若是代码的一部分须要字符串,则不能错误地将其传递为Int。

由于Swift是类型安全的,它在编译代码时执行类型检查,并将任何不匹配的类型标记为错误。这使您可以在开发过程当中尽早捕获和修复错误。

当您使用不一样类型的值时,类型检查能够帮助您避免错误。可是,这并不意味着必须指定声明的每一个常量和变量的类型。若是没有指定所需值的类型,Swift会使用类型推断来计算出适当的类型。类型推断使编译器在编译代码时可以经过检查提供的值自动推断特定表达式的类型。

因为有了类型推断,Swift须要的类型声明要比C或Objective-C等语言少得多。常量和变量仍然是显式类型的,可是指定它们的类型的大部分工做已经为您完成了。

在声明具备初始值的常量或变量时,类型推断特别有用。这一般是经过在声明常量或变量时为其分配一个文字值(或文字)来实现的。(文字值是直接出如今源代码中的值,例以下面示例中的42和3.14159。)

例如,若是你给一个新常量赋值为42而没有说明它是什么类型,Swift推断你但愿这个常量是一个整型数,由于你用一个看起来像整数的数字初始化了它:

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。

7 数字字面值

整型文字能够写成:

  • 无前缀的十进制数
  • 一种二进制数,前缀为0b
  • 带有0o前缀的八进制数
  • 带有0x前缀的十六进制数字

全部这些字面值都有一个十进制值17:

let decimalInteger = 17
let binaryInteger = 0b10001       // 17  二进制
let octalInteger = 0o21           // 17  八进制
let hexadecimalInteger = 0x11     // 17  十六进制
复制代码

浮点字面值能够是十进制(没有前缀)或十六进制(有0x前缀)。它们必须在小数点的两边都有一个数字(或十六进制数)。十进制浮点数还能够有一个可选的指数,由大写或小写e表示;十六进制浮点数必须有一个指数,用大写或小写p表示。

对于指数为exp的小数,底数乘以10exp:

  • 1.25e2 意味着 1.25 x 102, 或 125.0.
  • 1.25e-2 意味着 1.25 x 10-2, 或 0.0125.

对于指数为exp的十六进制数,底数乘以2exp:

  • 0xFp2 意味着 15 x 22, 或 60.0.
  • 0xFp-2 意味着 15 x 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
复制代码

8 数值类型转换

对代码中的全部通用整型常量和变量使用Int类型,即便已知它们是非负的。在平常状况中使用默认整数类型意味着整型常量和变量在代码中当即能够互操做,并将匹配整型文字值的推断类型。

只有在手头的任务特别须要其余整数类型时才使用它们,由于外部源显式地调整了数据大小,或者为了性能、内存使用或其余必要的优化。在这些状况下使用显式大小的类型有助于捕获任何意外值溢出,并隐式记录所使用数据的性质。

8.1 整数转换

能够存储在整型常量或变量中的数字的范围对于每种数值类型是不一样的。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
复制代码

因为每一个数值类型能够存储不一样范围的值,所以必须根据具体状况选择进行数值类型转换。这种可选择的方法能够防止隐藏的转换错误,并有助于在代码中显式地显示类型转换意图。

要将一种特定的数字类型转换为另外一种,可使用现有值初始化所需类型的新数字。在下面的示例中,常数2000的类型为UInt16,而常数1的类型为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提供初始化器的类型。扩展包括扩展示有类型以提供接受新类型(包括您本身的类型定义)的初始化器。

8.2 整数和浮点数转换

整数和浮点数类型之间的转换必须明确:

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi equals 3.14159, and is inferred to be of type Double
复制代码

在这里,常量3的值用于建立一个类型为Double的新值,这样加法的两边都是相同的类型。若是不进行此转换,则不容许添加。

浮点到整数的转换也必须明确。整数类型能够用双值或浮点值初始化:

let integerPi = Int(pi)
// integerPi equals 3, and is inferred to be of type Int
复制代码

以这种方式初始化新的整数值时,浮点值老是被截断。这意味着4.75变成4,而-3.9变成-3。

注意 组合数值常量和变量的规则与组合数值文字的规则不一样。文字值3能够直接添加到文字值0.14159,由于数字文字自己没有显式的in和of类型。它们的类型只有在由编译器计算时才会推断出来。

9 类型别名

类型别名定义现有类型的替代名称。能够经过typealias关键词定义类型别名。

当你想经过上下文更合适的名称来引用一个现有的类型时,类型别名颇有用,例如当从外部来源处理特定大小的数据时:

typealias AudioSample = UInt16
复制代码

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

var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0
复制代码

这里,AudioSample被定义为UInt16的别名。由于它是一个别名,调用AudioSample.min也就是调用UInt16.min,做为maxAmplitudeFound为0的值。

10 布尔值

Swift有一个基本的布尔类型,叫作Bool。布尔值被称为逻辑值,由于它们只能是真或假。Swift提供了两个布尔常量值true和false:

let orangesAreOrange = true
let turnipsAreDelicious = false
复制代码

orangesAreOrange和turnipsAreDelicious的类型已经被推断为Bool,由于它们是用布尔文字值初始化的。与上面的Int和Double同样,若是在建立常量或变量时就将它们设置为true或false,则不须要将它们声明为Bool。当Swift用其余已知类型的值初始化常量或变量时,类型推断有助于使Swift代码更加简洁和可读。

布尔值在处理条件语句时特别有用,好比if语句:

if turnipsAreDelicious {
    print("Mmm, tasty turnips!")
} else {
    print("Eww, turnips are horrible.")
}
// Prints "Eww, turnips are horrible."
复制代码

条件语句(如if语句)在控制流 中有更详细的介绍。

Swift的类型安全防止非布尔值被Bool替换。下面的示例报告了编译时错误:

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中的其余类型安全示例同样,这种方法避免了意外错误,并确保特定代码段的意图始终清晰。

11 元组

元组将多个值分组为一个复合值。元组中的值能够是任何类型,没必要彼此具备相同的类型。

在本例中,(404,“Not Found”)是描述HTTP状态代码的元组。HTTP状态码是web服务器在请求web页面时返回的特殊值。若是你请求的网页不存在,它会返回404 Not Found状态码。

let http404Error = (404, "Not Found")
// http404Error is of type (Int, String), and equals (404, "Not Found")
复制代码

元组将一个Int和一个字符串组合在一块儿,为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, 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"
复制代码

元组做为函数的返回值特别有用。试图检索web页面的函数可能会返回(Int, String)元组类型,以描述页面检索的成功或失败。经过返回具备两个不一样类型值的元组,与只能返回单个类型的单个值相比,函数提供了关于其结果的更有用的信息。有关更多信息,请参见具备多个返回值的函数

注意 元组对于相关值的简单组很是有用。它们不适合建立复杂的数据结构。若是数据结构可能更复杂,那么将其建模为类或结构体,而不是元组。有关更多信息,请参见结构体和类

12 可选值

在可能没有值的状况下使用可选值。可选的表示两种可能:要么有一个值,您能够展开可选的来访问该值,要么根本没有值。

注意 可选值的概念在C或Objective-C中不存在。Objective-C中最接近的是,从方法中返回nil的能力,不然会返回一个对象,nil表示“缺乏一个有效对象”。可是,这只适用于对象,而不适用于结构体、基本C类型或枚举值。对于这些类型,Objective-C方法一般返回一个特殊的值(好比NSNotFound)来表示没有值。这种假设方法的调用者知道有一个特殊的值要进行测试,而且记得检查它。Swift的可选值可让你指示任何类型的值是否存在,而不须要特殊的常量。

下面是一个示例,说明如何使用可选值来处理缺乏值的状况。Swift的Int类型有一个初始化器,它试图将字符串值转换为Int值。可是,不是每一个字符串均可以转换为整数。字符串“123”能够转换为数字值123,可是字符串“hello, world”没有明显的数字值能够转换。

下面的例子使用了初始化器来尝试将一个字符串转换成Int类型:

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber is inferred to be of type "Int?", or "optional Int"
复制代码

由于初始化器可能失败,它返回一个可选的整型,而不是整型。问号表示它包含的值是可选的,这意味着它可能包含一些Int值,也可能根本不包含值。它不能包含任何其余东西,好比Bool值或String值。要么是整数,要么什么都不是)

12.1 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 nil
复制代码

注意 Swift里的nil和OC里的不一样,OC里的nil是一个指针,指向一个不存在的对象。Swift里的nil不是一个指针--它是某种类型的值的缺失。任何类型的选项均可以设置为nil,而不只仅是对象类型。

12.2 If语句和强制解包

经过比较可选值和空值,可使用if语句来肯定可选值是否包含值。您可使用“等于”操做符(==)或“不等于”操做符(!=)执行比较。

若是可选值有值,会被认为不是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语句,请参阅控制流

注意 试着使用!访问不存在的可选值将触发运行时错误。在使用前,必定要确保一个可选的包含一个非nil值!强制打开它的值。

12.3 可选值绑定

您可使用可选绑定来查明可选项是否包含值,若是包含值,则使该值做为临时常量或变量可用。可选绑定能够与if和while语句一块儿使用,以检查可选语句中的值,并将该值提取为常量或变量,这是单个操做的一部分。if和while语句在控制流中有更详细的描述。

使用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)返回的可选整数包含一个值,则设置一个名为actualNumber的新常量为可选整数中包含的值。”

若是转换成功,就能够在If语句的第一个分支中使用actualNumber常数。它已经用可选项中包含的值进行了初始化,所以不须要使用!后缀来访问其值。在本例中,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中所述。

12.4 隐式解包可选值

如上所述,可选值表示常量或变量容许“无值”。可选值能够用if语句检查值是否存在,也能够用可选绑定有条件地解除包装,以访问可选值(若是存在)。

有时候从程序结构上,可选值在第一次设置后,总会有一个值。在这些状况下,在访问可选值的时候,移除检查和解包的操做是有效的,由于它老是安全地假定有一个值。

这些类型的可选值被定义为隐式的解包可选值。经过在但愿使类型可选的后面放置感叹号(String!)而不是问号(String?)来编写可选的隐式解包。在使用可选名称时,不要在其后放置感叹号,而应在声明可选类型时在其后放置感叹号。

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation point

let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // no need for an exclamation point
复制代码

(注:这里终于清楚地明白了?和!的用法,说明学一门语言仍是要从官方文档开始)

你能够将可选的隐式解包看做容许可选的对象在须要时强制解包。当你使用隐式解包装可选值时,Swift首先尝试将其做为普通可选值使用;若是不能做为可选值使用,Swift强制打开该值。在上面的代码中,可选值assumedString在将其值分配给implicitString以前被强制解除包装,由于implicitString具备显式的、非可选的字符串类型。在下面的代码中,optionalString没有显式类型,因此它是一个普通的可选类型。

let optionalString = assumedString
// The type of optionalString is "String?" and assumedString isn't force-unwrapped. 复制代码

若是一个可选的隐式解包是nil,而且您尝试访问它的包装值,您将触发一个运行时错误。其结果与在不包含值的普通可选项后放置感叹号彻底相同。

你能够检查一个可选的隐式解包是否为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值,请始终使用普通的可选类型。

13 错误处理

使用错误处理来响应程序在执行过程当中可能遇到的错误条件。

可选值可使用值的存在或不存在来传递函数的成功或失败,与之相反,错误处理容许您肯定失败的根本缘由,并在必要时将错误传播到程序的另外一部分。

当函数遇到错误条件时,它会抛出错误。而后,函数的调用者能够捕获错误并做出适当的响应。

func canThrowAnError() throws {
    // this function may or 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 SandwichError.outOfCleanDishes {
    washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
    buyGroceries(ingredients)
}
复制代码

在本例中,若是没有干净的盘子或缺乏任何配料,makeASandwich()函数将抛出一个错误。由于makeASandwich()可能抛出错误,因此函数调用被包装在一个try表达式中。经过将函数调用包装在do语句中,抛出的任何错误都将传播到提供的catch子句。

若是没有抛出错误,则调用eatASandwich()函数。若是抛出一个错误,而且它与sandwichError匹配。在outOfCleanDishes状况下,则调用washDishes()函数。若是抛出一个错误,而且它与sandwichError匹配。missingcomponents状况下,使用catch模式捕获的相关[String]值调用buyGroceries(_:)函数。

错误处理中更详细地介绍了抛出、捕获和传播错误。

14 断言和先决条件

断言和前置条件是在运行时进行的检查。在执行任何其余代码以前,使用它们确保知足必要条件。若是断言或先决条件中的布尔值为真,则代码执行照常继续。若是条件计算为false,则程序的当前状态无效;代码执行结束,应用程序终止。

在编码时,可使用断言和先决条件来表示所做的假设和指望,所以能够将它们做为代码的一部分。断言帮助您在开发期间发现错误和不正确的假设,而前提条件帮助您检测生产中的问题。

除了在运行时验证您的指望以外,断言和先决条件也成为代码中有用的文档形式。与上面错误处理中讨论的错误条件不一样,断言和前置条件不会用于可恢复的或预期的错误。因为失败的断言或先决条件指示无效的程序状态,所以没法捕获失败的断言。

使用断言和先决条件不能替代以不太可能出现无效条件的方式设计代码。可是,使用它们强制执行有效的数据和状态会致使应用程序在出现无效状态时可预测地终止,并有助于使问题更容易调试。在检测到无效状态时当即中止执行还有助于限制由该无效状态形成的损害。

断言和前提条件的区别在于它们被检查的时候:断言只在调试构建中被检查,可是前提条件在调试和生产构建中都被检查。在生产构建中,不计算断言中的条件。这意味着您能够在开发过程当中使用任意数量的断言,而不会影响生产中的性能。

14.1 用断言调试

经过从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的值为真(即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.")
}
复制代码

14.2 执行先决条件

只要条件可能为假,但必须为真,代码才能继续执行,就使用前置条件。例如,使用先决条件检查下标是否超出范围,或检查函数是否传递了有效值。

你能够经过调用 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的其余状况之一处理。

注意 若是以unchecked模式(-Ounchecked)编译,则不会检查前置条件。编译器假设前提条件老是为真,并相应地优化代码。然而,不管优化设置如何,fatalError(_:file:line:)函数老是会中止执行。

您能够在原型和早期开发期间使用fatalError(_:file:line:)函数为还没有实现的功能建立存根,方法是编写fatalError(“未实现”)做为存根实现。由于与断言或先决条件不一样,致命错误历来没有被优化出来,因此能够确定,若是遇到存根实现,执行老是会中止。

总结

经过这一章节的学习,能够了解Swift基础的一些知识以及它特有的一些功能:

  • 如何去定义常量和变量
  • 如何添加注释:和OC差很少,不过比OC多了个**"""**注释多行的符号
  • 分号的做用:单行上写多行代码时须要加上
  • 整型、浮点型和布尔值
  • Swift特有的类型安全和类型推断:这个真的很实用,可提升开发效率
  • 数字字面值和数值类型转换
  • 类型别名
  • 元组
  • 可选值:?和!的用法,以及可选绑定if-let的用法
  • 错误处理:try-catch和do-try-catch
  • 断言和先决条件 这里就不详细说明各个知识点了,能够从上面的内容中寻找答案。最后,若是你们喜欢的话,能够给个star哦,有你的支持,就是个人动力!

上一章节:开篇
下一章节:基本操做

参考文档:Swift - The Basics

相关文章
相关标签/搜索