★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公众号:山青咏芝(shanqingyongzhi)
➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/)
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:https://www.cnblogs.com/strengthen/p/9711776.html
➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。
➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
Swift是iOS,macOS,watchOS和tvOS应用程序开发的新编程语言。尽管如此,Swift的许多部分对您在C和Objective-C中的开发经验都很熟悉。
雨燕提供了自己的所有基本C和Objective-C类型的版本,包括Int
为整数,Double
并Float
为浮点值,Bool
布尔值,并String
为文本数据。雨燕还提供了三种主要类型的集合强大的版本Array
,Set
和Dictionary
,如在集合类型。
与C一样,Swift使用变量来存储和通过标识名称引用值。Swift还广泛使用其值无法更改的变量。这些被称为常量,并且比C中的常量更强大。当您使用不需要更改的值时,在整个Swift中使用常量来使代码更安全,更清晰。
除了熟悉的类型之外,Swift还引入了Objective-C中没有的高级类型,例如元组。元组使您可以创建和传递值的分组。您可以使用元组将函数中的多个值作为单个复合值返回。
Swift还引入了可选类型,它们处理缺少值。选配说要么“有是一个值,它等于X ”或“有没有一个价值可言”。使用optionals类似于nil
在Objective-C中使用指针,但它们适用于任何类型,而不仅仅是类。它们不仅比nil
Objective-C中的指针更安全,更具表现力,它们是Swift许多最强大功能的核心。
Swift是一种类型安全的语言,这意味着该语言可以帮助您清楚代码可以使用的值的类型。如果您的部分代码需要a String
,则类型安全会阻止您Int
错误地传递它。同样,类型安全性可防止您意外地将可选项传递String
给需要非可选代码的代码String
。类型安全性可帮助您在开发过程中尽早捕获并修复错误。
常量和变量
常量和变量将名称(例如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
关键字将其声明为常量。仅使用变量存储需要更改的值。
输入注释
声明常量或变量时,可以提供类型注释,以清楚常量或变量可以存储的值的类型。通过在常量或变量名称后面放置冒号,后跟空格,后跟要使用的类型的名称来编写类型注释。
此示例为名为变量的变量提供类型注释welcomeMessage
,以指示变量可以存储String
值:
- var welcomeMessage: String
声明中的冒号表示“......类型......”,因此上面的代码可以解读为:
“声明一个名为welcomeMessage
type 的变量String
。”
短语“类型String
”的意思是“可以存储任何String
值。”可以将其视为可以存储的“事物类型”(或“事物类型”)。
welcomeMessage
现在可以将变量设置为任何字符串值而不会出现错误:
- welcomeMessage = "Hello"
您可以在一行上定义相同类型的多个相关变量(以逗号分隔),在最终变量名称后面使用单个类型注释:
- var red, green, blue: Double
注意
您很少需要在实践中编写类型注释。如果在定义的点处为常量或变量提供初始值,则Swift几乎总是可以推断出用于该常量或变量的类型,如类型安全和类型推断中所述。在welcomeMessage
上面的示例中,没有提供初始值,因此welcomeMessage
使用类型注释指定变量的类型,而不是从初始值推断。
命名常量和变量
常量和变量名称几乎可以包含任何字符,包括Unicode字符:
- let π = 3.14159
- let 你好 = "你好世界"
- let ?? = "dogcow"
常量和变量名称不能包含空格字符,数学符号,箭头,专用Unicode标量值或行和框绘制字符。它们也不能以数字开头,尽管数字可能包含在名称的其他地方。
一旦声明了某个类型的常量或变量,就不能再使用相同的名称声明它,或者将其更改为存储不同类型的值。也不能将常量变为变量或变量变为常量。
注意
如果您需要为常量或变量提供与保留的Swift关键字相同的名称,请在将关键字`
用作名称时用backticks()包围该关键字。但是,除非您绝对没有选择,否则请避免使用关键字作为名称。
您可以将现有变量的值更改为兼容类型的另一个值。在此示例中,值的值friendlyWelcome
更改"Hello!"
为"Bonjour!"
:
- var friendlyWelcome = "Hello!"
- friendlyWelcome = "Bonjour!"
- // friendlyWelcome is now "Bonjour!"
与变量不同,常量的值在设置后不能更改。编译代码时,尝试执行此操作会报告为错误:
- let languageName = "Swift"
- languageName = "Swift++"
- // This is a compile-time error: languageName cannot be changed.
打印常数和变量
您可以使用以下print(_:separator:terminator:)
函数打印常量或变量的当前值:
- print(friendlyWelcome)
- // Prints "Bonjour!"
该print(_:separator:terminator:)
函数是一个全局函数,它将一个或多个值输出到适当的输出。例如,在Xcode中,该print(_:separator:terminator:)
函数在Xcode的“控制台”窗格中打印其输出。该separator
和terminator
参数都有默认值,所以当你调用这个函数,你可以忽略它们。默认情况下,该函数通过添加换行符来终止它打印的行。要在其后打印没有换行符的值,请将空字符串作为终止符传递 - 例如,。有关具有默认值的参数的信息,请参阅默认参数值。print(someValue, terminator: "")
Swift使用字符串插值将常量或变量的名称包含在较长字符串中作为占位符,并提示Swift将其替换为该常量或变量的当前值。将名称括在括号中,并在左括号前用反斜杠转义它:
- print("The current value of friendlyWelcome is \(friendlyWelcome)")
- // Prints "The current value of friendlyWelcome is Bonjour!"
注意
你可以用串插中使用的所有选项中描述字符串插值。
评论
使用注释在代码中包含不可执行文本,作为对自己的注释或提醒。编译代码时,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. */
嵌套的多行注释使您能够快速,轻松地注释掉大块代码,即使代码已经包含多行注释。
分号
与许多其他语言不同,Swift不要求您;
在代码中的每个语句之后编写分号(),但如果您愿意,也可以这样做。但是,如果要在一行上写多个单独的语句,则需要使用分号:
- let cat = "?"; print(cat)
- // Prints "?"
整型
整数是整数,没有小数分量,例如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
在上面的示例中),因此可以在表达式中与其他相同类型的值一起使用。
诠释
在大多数情况下,您不需要选择要在代码中使用的特定大小的整数。Swift提供了一个额外的整数类型,Int
它与当前平台的本机字大小相同:
- 在32位平台上,
Int
大小与之相同Int32
。 - 在64位平台上,
Int
大小与之相同Int64
。
除非您需要使用特定大小的整数,否则请始终Int
在代码中使用整数值。这有助于代码一致性和互操作性。即使在32位平台上,Int
也可以在-2,147,483,648
和之间存储任何值2,147,483,647
,并且对于许多整数范围来说足够大。
UINT
Swift还提供无符号整数类型,UInt
其大小与当前平台的本机字大小相同:
- 在32位平台上,
UInt
大小与之相同UInt32
。 - 在64位平台上,
UInt
大小与之相同UInt64
。
注意
使用UInt
只有当你特别需要具有相同大小的平台的本地字大小的无符号整型。如果不是这种情况,Int
则优选,即使已知要存储的值是非负的。Int
对整数值的一致使用有助于代码互操作性,避免在不同数字类型之间进行转换,并匹配整数类型推断,如类型安全和类型推断中所述。
浮点数字
浮点数是具有小数部分的数字,例如3.14159
,0.1
,和-273.15
。
浮点类型可以表示比整数类型更宽范围的值,并且可以存储比可以存储在一个更大或更小的数字Int
。Swift提供了两种带符号的浮点数类型:
Double
表示64位浮点数。Float
表示32位浮点数。
注意
Double
具有至少15位十进制数的精度,而精度Float
可以小至6位十进制数。要使用的适当浮点类型取决于您在代码中使用的值的性质和范围。在任何一种类型适当的情况下,Double
首选。
类型安全和类型推断
Swift是一种类型安全的语言。类型安全语言鼓励您清楚代码可以使用的值的类型。如果您的部分代码需要a String
,则不能Int
错误地传递它。
因为Swift是类型安全的,所以它在编译代码时执行类型检查,并将任何不匹配的类型标记为错误。这使您能够在开发过程中尽早捕获并修复错误。
当您使用不同类型的值时,类型检查可帮助您避免错误。但是,这并不意味着您必须指定您声明的每个常量和变量的类型。如果未指定所需的值类型,Swift将使用类型推断来计算出适当的类型。类型推断使编译器能够在编译代码时自动推断出特定表达式的类型,只需检查您提供的值即可。
由于类型推断,Swift所需的类型声明远远少于C或Objective-C等语言。常量和变量仍然是显式类型的,但指定其类型的大部分工作都是为您完成的。
当您使用初始值声明常量或变量时,类型推断特别有用。这通常通过在声明它的点处为常量或变量指定文字值(或文字)来完成。(A字面值是直接出现在源代码中,如一个值42
和3.14159
在下面的例子。)
例如,如果您将一个文字值分配42
给一个新常量而不说它是什么类型,Swift推断您希望该常量为a 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
从浮点文字的存在作为加法的一部分推断出适当的输出类型。
数字文字
整数文字可以写成:
- 一个十进制数,无前缀
- 一个二进制数,有
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 hexadecimal notation
浮点文字可以是十进制(没有前缀)或十六进制(带0x
前缀)。它们必须始终在小数点的两边都有一个数字(或十六进制数字)。十进制浮点数也可以有一个可选的指数,用大写或小写表示e
; 十六进制浮点数必须有一个指数,用大写或小写表示p
。
对于指数为的十进制数exp
,基数乘以10 exp:
1.25e2
表示1.25 x 10 2,或125.0
。1.25e-2
表示1.25 x 10 -2,或0.0125
。
对于指数为的十六进制数exp
,基数乘以2 exp:
0xFp2
指15 x 2 2,或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
数字类型转换
将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
,所以允许添加。输出constant(twoThousandAndOne
)被推断为类型UInt16
,因为它是两个UInt16
值的总和。
SomeType(ofInitialValue)
是调用Swift类型的初始值设定项并传入初始值的默认方式。在幕后,UInt16
有一个接受UInt8
值的初始值设定项,因此这个初始化程序用于UInt16
从现有的初始化程序中创建一个新的UInt8
。你不能在这里传递任何类型,但它必须是一个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 maxAmplitudeFound = AudioSample.min
- // maxAmplitudeFound is now 0
这里,AudioSample
被定义为别名UInt16
。因为它是一个别名,调用AudioSample.min
实际调用UInt16.min
,它提供的初始值0
的maxAmplitudeFound
变量。
布尔
Swift有一个基本的布尔类型,叫做Bool
。布尔值被称为逻辑,因为它们只能是true或false。Swift提供了两个布尔常量值,true
并且false
:
- let orangesAreOrange = true
- let turnipsAreDelicious = false
从使用布尔文字值初始化它们的事实推断出的类型orangesAreOrange
和turnipsAreDelicious
推断Bool
。正如Int
和Double
上面,你并不需要声明常量或变量Bool
,如果将其设置为true
或false
为您创建它们尽快。类型推断有助于使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
- }
比较的结果是类型,因此第二个示例通过了类型检查。类似的比较在Basic Operators中讨论。i == 1
Bool
i == 1
与Swift中的其他类型安全示例一样,此方法可避免意外错误,并确保特定代码段的意图始终清晰。
元组
元组将多个值分组为单个复合值。元组中的值可以是任何类型,并且不必彼此具有相同的类型。
在此示例中,是一个描述HTTP状态代码的元组。HTTP状态代码是Web服务器在您请求网页时返回的特殊值。如果您请求不存在的网页,则会返回状态代码。(404, "Not Found")
404 Not Found
- let http404Error = (404, "Not Found")
- // http404Error is of type (Int, String), and equals (404, "Not Found")
的元组基团一起的和,得到的HTTP状态代码两个独立的值:一个数字和一个人类可读的描述。它可以被描述为“类型元组”。(404, "Not Found")
Int
String
(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)
注意
元组对于简单的相关值组很有用。它们不适合创建复杂的数据结构。如果您的数据结构可能更复杂,请将其建模为类或结构,而不是元组。有关更多信息,请参阅结构和类。
选配
您可以在可能缺少值的情况下使用选项。可选的代表两种可能性:要么有是一个值,你可以解开可选的访问值,或者有没有价值可言。
注意
选项的概念在C或Objective-C中不存在。Objective-C中最接近的东西是nil
从一个方法返回的能力,否则返回一个对象,nil
意思是“缺少一个有效的对象。”但是,这只适用于对象 - 它不适用于结构,基本C类型或枚举值。对于这些类型,Objective-C方法通常返回特殊值(例如NSNotFound
)以指示缺少值。这种方法假定方法的调用者知道有一个特殊的值来测试并记得检查它。Swift的选项让你可以指出任何类型都没有值,而不需要特殊的常量。
这是一个如何使用选项来应对缺少价值的例子。Swift的Int
类型有一个初始化程序,它试图将String
值转换为Int
值。但是,并非每个字符串都可以转换为整数。字符串"123"
可以转换为数值123
,但字符串没有明显的数值可以转换为。"hello, world"
下面的示例使用初始化程序尝试将a 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
:
- 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
与nil
Objective-C不同。在Objective-C中,nil
是一个指向不存在的对象的指针。在Swift中,nil
不是指针 - 它缺少某种类型的值。可以将任何类型的选项设置为nil
,而不仅仅是对象类型。
如果声明和强制解包
您可以使用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
语句的更多信息,请参阅控制流。
注意
尝试使用!
访问不存在的可选值会触发运行时错误。nil
在使用!
强制解包其值之前,请务必确保可选项包含非值。
可选绑定
您使用可选绑定来确定可选项是否包含值,如果是,则使该值可用作临时常量或变量。可选绑定可与with if
和while
语句一起使用,以检查可选内部的值,并将该值提取为常量或变量,作为单个操作的一部分。if
和控制流程while
中更详细地描述了语句。
为if
语句编写可选绑定,如下所示:
- if let constantName = someOptional {
- statements
- }
您可以possibleNumber
从Optionals部分重写示例以使用可选绑定而不是强制解包:
- 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
返回的可选项Int(possibleNumber)
包含一个值,则设置一个新的常量,调用可选的包含值actualNumber
。”
如果转换成功,则该actualNumber
常量可在if
语句的第一个分支中使用。它已经被初始化与包含在值内可选的,因此没有必要使用!
后缀来访问它的价值。在此示例中,actualNumber
仅用于打印转换结果。
您可以将常量和变量与可选绑定一起使用。如果你想操纵语句actualNumber
的第一个分支中的值if
,你可以改为编写,并且可选的包含的值将作为变量而不是常量使用。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中所述。
隐含的未包装的选项
如上所述,选项表示允许常量或变量具有“无值”。可以使用if
语句检查Optionals 以查看是否存在值,并且可以使用可选绑定有条件地解包,以访问可选的值(如果存在)。
有时从程序的结构可以清楚地看出,在首次设置该值之后,可选项将始终具有值。在这些情况下,每次访问时都不需要检查和解包可选项的值,因为可以安全地假设它始终具有值。
这些类型的选项被定义为隐式解包的选项。您通过在要使其成为可选的类型之后放置感叹号(String!
)而不是问号()来编写隐式展开的可String?
选项。
当在首次定义可选项之后立即确认可选的值存在时,隐式展开的选项很有用,并且可以假定在此后的每个点都存在。Swift中隐式解包选项的主要用途是在类初始化期间,如Unowned References和Implicitly Unwrapped Optional Properties中所述。
隐式展开的可选项是幕后的常规可选项,但也可以像非可选值一样使用,而无需在每次访问时解包可选值。以下示例显示了在将其包装值作为显式访问时,可选字符串与隐式解包的可选字符串之间的行为差异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
你可以想到一个隐式解包的可选项,因为它允许在任何时候自动解包可选项。每次使用时,都不要在可选项的名称后面放置感叹号,而是在声明它时在可选项的类型后面添加一个感叹号。
注意
如果隐式解包的可选项是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
在变量的生命周期内检查值,请始终使用普通的可选类型。
错误处理
您使用错误处理来响应程序在执行期间可能遇到的错误情况。
与可以使用值的存在或不存在来传递函数成功或失败的选项相比,错误处理允许您确定失败的根本原因,并在必要时将错误传播到程序的另一部分。
当函数遇到错误条件时,它会抛出错误。然后该函数的调用者可以捕获错误并做出适当的响应。
- 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.missingIngredients
大小写匹配,则buyGroceries(_:)
调用该函数,并使用模式[String]
捕获的关联值catch
。
错误处理中更详细地介绍了抛出,捕获和传播错误。
断言和先决条件
断言和前置条件是在运行时发生的检查。在执行任何进一步的代码之前,您可以使用它们来确保满足基本条件。如果断言或前置条件中的布尔条件求值为true
,则代码执行将照常继续。如果条件评估为false
,则程序的当前状态无效; 代码执行结束,您的应用程序终止。
您使用断言和前置条件来表达您所做的假设以及编码时的期望,因此您可以将它们作为代码的一部分包含在内。断言可帮助您在开发过程中发现错误和错误假设,并且先决条件可帮助您检测生产中的问题。
除了在运行时验证您的期望之外,断言和先决条件也成为代码中有用的文档形式。与上面的错误处理中讨论的错误条件不同,断言和前置条件不用于可恢复或预期的错误。因为失败的断言或前提条件表明程序状态无效,所以无法捕获失败的断言。
使用断言和前置条件并不能代替以不可能出现无效条件的方式设计代码。但是,如果发生无效状态,使用它们来强制执行有效数据和状态会导致应用程序以更可预测的方式终止,并有助于使问题更易于调试。一旦检测到无效状态就停止执行也有助于限制由该无效状态造成的损害。
断言和前置条件之间的区别在于它们的检查时间:仅在调试版本中检查断言,但在调试和生成版本中都会检查前置条件。在生产版本中,不会评估断言中的条件。这意味着您可以在开发过程中使用任意数量的断言,而不会影响生产中的性能。
使用断言进行调试
您可以通过assert(_:_:file:line:)
从Swift标准库中调用函数来编写断言。您将此函数传递给一个表达式,该表达式求值为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.")
- }
执行先决条件
只要条件可能为false,就使用前置条件,但对于代码继续执行必须确实为真。例如,使用前置条件检查下标是否超出范围,或检查函数是否已传递有效值。
你通过调用precondition(_:_:file:line:)
函数编写一个前提条件。您将此函数传递给一个表达式,该表达式求值为true
或者false
,如果条件的结果为,则显示一条消息false
。例如:
- // In the implementation of a subscript...
- precondition(index > 0, "Index must be greater than zero.")
您也可以调用该preconditionFailure(_:file:line:)
函数来指示发生了故障 - 例如,如果采用了交换机的默认情况,但所有有效的输入数据应该由交换机的其他情况之一处理。
注意
如果以unchecked mode(-Ounchecked
)编译,则不检查前置条件。编译器假定前置条件始终为true,并相应地优化代码。但是,fatalError(_:file:line:)
无论优化设置如何,该功能始终会暂停执行。
您可以fatalError(_:file:line:)
在原型设计和早期开发期间使用该功能,通过编写fatalError("Unimplemented")
存根实现来创建尚未实现的功能的存根。因为致命错误永远不会被优化,与断言或前置条件不同,您可以确保执行始终在遇到存根实现时停止。
所有评论(0)