Swift语法图
2021-03-29 15:37:10 0 举报
AI智能生成
swift语法流程图
作者其他创作
大纲/内容
构造过程
----
析构过程
可选链
错误处理
类型转换
嵌套类型
扩展
协议
泛型
不透明类型
自动引用计数
内存安全
访问控制
高级运算符
Swift 语法图大纲
基础知识
变量与常量
用 var 来声明变量
用 let 来声明常量
let maximumNumberOfLoginAttempts = 10var currentLoginAttempt = 0
注意
常量与变量名不能包含数学符号,箭头,保留的(或者非法的)Unicode 码位,连线与制表符。也不能以数字开头,但是可以在常量与变量名的其他地方包含数字。一旦你将常量或者变量声明为确定的类型,你就不能使用相同的名字再次进行声明,或者改变其存储的值的类型。同时,你也不能将常量与变量进行互转。
如果你需要使用与 Swift 保留关键字相同的名称作为常量或者变量名,你可以使用反引号(`)将关键字包围的方式将其作为名字使用。无论如何,你应当避免使用关键字作为常量或变量名,除非你别无选择。
输出常量和变量
Swift 用字符串插值(string interpolation)的方式把常量名或者变量名当做占位符加入到长字符串中,Swift 会用当前常量或变量的值替换这些占位符。将常量或变量名放入圆括号中,并在开括号前使用反斜杠将其转义:print(\"The current value of friendlyWelcome is \\(friendlyWelcome)\")// 输出“The current value of friendlyWelcome is Bonjour!”
类型注解
var welcomeMessage: String
这个例子给 welcomeMessage 变量添加了类型注解,表示这个变量可以存储 String 类型的值:声明中的冒号代表着“是...类型”,所以这行代码可以被理解为:“声明一个类型为 String ,名字为 welcomeMessage 的变量。”“类型为 String ”的意思是“可以存储任意 String 类型的值。”
注释
有一种情况下必须要用分号,即你打算在同一行内写多条独立的语句:let cat = \"🐱\"; print(cat)// 输出“🐱”
单行注释以双正斜杠(//)作为起始标记:/* 这也是一个注释,但是是多行的 */
可以先生成一个多行注释块,然后在这个注释块之中再嵌套成第二个多行注释。终止注释时先插入第二个注释块的终止标记,然后再插入第一个注释块的终止标记/* 这是第一个多行注释的开头/* 这是第二个被嵌套的多行注释 */这是第一个多行注释的结尾 */
整数
整数就是没有小数部分的数字,比如 42 和 -23 。整数可以是 有符号(正、负、零)或者 无符号(正、零)。Swift 提供了8、16、32和64位的有符号和无符号整数类型比如8位无符号整数类型是 UInt8,32位有符号整数类型是 Int32
整数范围
let minValue = UInt8.min // minValue 为 0,是 UInt8 类型let maxValue = UInt8.max // maxValue 为 255,是 UInt8 类型
Int
Swift 提供了一个特殊的整数类型 Int,长度与当前平台的原生字长相同:在32位平台上,Int 和 Int32 长度相同。在64位平台上,Int 和 Int64 长度相同。
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。
类型安全(type safe)和类型推断(type inference)
let meaningOfLife = 42// meaningOfLife 会被推测为 Int 类型
let pi = 3.14159// pi 会被推测为 Double 类型当推断浮点数的类型时,Swift 总是会选择 Double 而不是 Float。
let anotherPi = 3 + 0.14159// anotherPi 会被推测为 Double 类型如果表达式中同时出现了整数和浮点数,会被推断为 Double 类型:
数值类型转换
整数字面量可以被写作:一个十进制数,没有前缀一个二进制数,前缀是 0b一个八进制数,前缀是 0o一个十六进制数,前缀是 0xlet decimalInteger = 17let binaryInteger = 0b10001 // 二进制的17let octalInteger = 0o21 // 八进制的17let hexadecimalInteger = 0x11 // 十六进制的17
浮点字面量可以是十进制(没有前缀)或者是十六进制(前缀是 0x )。小数点两边必须有至少一个十进制数字(或者是十六进制的数字)。十进制浮点数也可以有一个可选的指数(exponent),通过大写或者小写的 e 来指定;十六进制浮点数必须有一个指数,通过大写或者小写的 p 来指定。下面的这些浮点字面量都等于十进制的 12.1875:let decimalDouble = 12.1875let exponentDouble = 1.21875e1let hexadecimalDouble = 0xC.3p0
通常来讲,即使代码中的整数常量和变量已知非负,也请使用 Int 类型。总是使用默认的整数类型可以保证你的整数常量和变量可以直接被复用并且可以匹配整数类字面量的类型推断。只有在必要的时候才使用其他整数类型,
整数转换
Int8 类型的常量或者变量可以存储的数字范围是 -128~127,而 UInt8 类型的常量或者变量能存储的数字范围是 0~255。如果数字超出了常量或者变量可存储的范围,编译的时候会报错:let cannotBeNegative: UInt8 = -1// UInt8 类型不能存储负数,所以会报错let tooBig: Int8 = Int8.max + 1// Int8 类型不能存储超过最大值的数,所以会报错因为它们类型不同。所以要调用 UInt16(one) 来创建一个新的 UInt16 数字并用 one 的值来初始化,然后使用这个新数字来计算:let twoThousand: UInt16 = 2_000let one: UInt8 = 1let twoThousandAndOne = twoThousand + UInt16(one)
整数和浮点数转换
整数和浮点数的转换必须显式指定类型:let three = 3let pointOneFourOneFiveNine = 0.14159let pi = Double(three) + pointOneFourOneFiveNine// pi 等于 3.14159,所以被推测为 Double 类型
浮点数到整数的反向转换同样行,整数类型可以用 Double 或者 Float 类型来初始化:let integerPi = Int(pi)// integerPi 等于 3,所以被推测为 Int 类型
类型别名(type aliases)
就是给现有类型定义另一个名字。你可以使用 typealias 关键字来定义类型别名。typealias AudioSample = UInt16var maxAmplitudeFound = AudioSample.min// maxAmplitudeFound 现在是 0 ,AudioSample 被定义为 UInt16 的一个别名。因为它是别名,AudioSample.min 实际上是 UInt16.min,所以会给 maxAmplitudeFound 赋一个初值 0。
布尔值(Boolean)
Swift 有两个布尔常量,true 和 false:let orangesAreOrange = truelet turnipsAreDelicious = false
if turnipsAreDelicious { print(\
let i = 1if i == 1 { // 这个例子会编译成功}
元组(tuples)
可以通过下标来访问元组中的单个元素,下标从零开始:print(\"The status code is \\(http404Error.0)\")// 输出“The status code is 404”print(\"The status message is \\(http404Error.1)\")// 输出“The status message is Not Found”
给元组中的元素命名后,你可以通过名字来获取这些元素的值:print(\"The status code is \\(http200Status.statusCode)\")// 输出“The status code is 200”print(\"The status message is \\(http200Status.description)\")// 输出“The status message is OK”
可选类型(optionals)
let possibleNumber = \"123\"let convertedNumber = Int(possibleNumber)// convertedNumber 被推测为类型 \"Int?\", 或者类型 \"optional Int\"
nil
可以给可选变量赋值为 nil 来表示它没有值:var serverResponseCode: Int? = 404// serverResponseCode 包含一个可选的 Int 值 404serverResponseCode = nil// serverResponseCode 现在不包含值注意nil 不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为 nil:var surveyAnswer: String?// surveyAnswer 被自动设置为 nil
注意Swift 的 nil 和 Objective-C 中的 nil 并不一样。在 Objective-C 中,nil 是一个指向不存在对象的指针。在 Swift 中,nil 不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为 nil,不只是对象类型。
if 语句以及强制解析
可以使用 if 语句和 nil 比较来判断一个可选值是否包含值。你可以使用“相等”(==)或“不等”(!=)来执行比较。如果可选类型有值,它将不等于 nil:if convertedNumber != nil { print(\"convertedNumber contains some integer value.\")}// 输出“convertedNumber contains some integer value.”
当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(!)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的强制解析(forced unwrapping):if convertedNumber != nil { print(\"convertedNumber has an integer value of \\(convertedNumber!).\")}// 输出“convertedNumber has an integer value of 123.”
注意 使用 ! 来获取一个不存在的可选值会导致运行时错误。使用 ! 来强制解析值之前,一定要确定可选包含一个非 nil 的值。
可选绑定(optional binding)
是用来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在 if 和 while 语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量。
if let actualNumber = Int(possibleNumber) { print(\"\\'\\(possibleNumber)\\' has an integer value of \\(actualNumber)\")} else { print(\"\\'\\(possibleNumber)\\' could not be converted to an integer\")}// 输出“'123' has an integer value of 123”这段代码可以被理解为:“如果 Int(possibleNumber) 返回的可选 Int 包含一个值,创建一个叫做 actualNumber 的新常量并将可选包含的值赋给它。”如果转换成功,actualNumber 常量可以在 if 语句的第一个分支中使用。它已经被可选类型 包含的 值初始化过,所以不需要再使用 ! 后缀来获取它的值。在这个例子中,actualNumber 只被用来输出转换结果。你可以在可选绑定中使用常量和变量。如果你想在 if 语句的第一个分支中操作 actualNumber 的值,你可以改成 if var actualNumber,这样可选类型包含的值就会被赋给一个变量而非常量。你可以包含多个可选绑定或多个布尔条件在一个 if 语句中,只要使用逗号分开就行。只要有任意一个可选绑定的值为 nil,或者任意一个布尔条件为 false,则整个 if 条件判断为 false。下面的两个 if 语句是等价的:if let firstNumber = Int(\"4\
注意在 if 条件语句中使用常量和变量来创建一个可选绑定,仅在 if 语句的句中(body)中才能获取到值。相反,在 guard 语句中使用常量和变量来创建一个可选绑定,仅在 guard 语句外且在语句后才能获取到值
隐式解析可选类型(implicitly unwrapped optionals)
可选类型暗示了常量或者变量可以“没有值”。可选可以通过 if 语句来判断是否有值,如果有值的话可以通过可选绑定来解析值。有时候在程序架构中,第一次被赋值之后,可以确定一个可选类型总会有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。这种类型的可选状态被定义为隐式解析可选类型(implicitly unwrapped optionals)。把想要用作可选的类型的后面的问号(String?)改成感叹号(String!)来声明一个隐式解析可选类型。与其在使用时把感叹号放在可选类型的名称的后面,你可以在定义它时,直接把感叹号放在可选类型的后面。当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型 String 和隐式解析可选类型 String 之间的区别:let possibleString: String? = \"An optional string.\"let forcedString: String = possibleString! // 需要感叹号来获取值let assumedString: String! = \"An implicitly unwrapped optional string.\"let implicitString: String = assumedString // 不需要感叹号
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。当你使用一个隐式解析可选值时,Swift 首先会把它当作普通的可选值;如果它不能被当成可选类型使用,Swift 会强制解析可选值。在以上的代码中,可选值 assumedString 在把自己的值赋给 implicitString 之前会被强制解析,原因是 implicitString 本身的类型是非可选类型的 String。在下面的代码中,optionalString 并没有显式的数据类型。那么根据类型推断,它就是一个普通的可选类型。let optionalString = assumedString// optionalString 的类型是 \"String?\",assumedString 也没有被强制解析。
如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个感叹号一样。你可以把隐式解析可选类型当做普通可选类型来判断它是否包含值:if assumedString != nil { print(assumedString!)}// 输出“An implicitly unwrapped optional string.”也可以在可选绑定中使用隐式解析可选类型来检查并解析它的值:if let definiteString = assumedString { print(definiteString)}// 输出“An implicitly unwrapped optional string.”
注意如果一个变量之后可能变成 nil 的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是 nil 的话,请使用普通可选类型。
错误处理(error handling)
相对于可选中运用值的存在与缺失来表达函数的成功与失败,错误处理可以推断失败的原因,并传播至程序的其他部分。当一个函数遇到错误条件,它能报错。调用函数的地方能抛出错误消息并合理处理。func canThrowAnError() throws { // 这个函数有可能抛出错误}一个函数可以通过在声明中添加 throws 关键词来抛出错误消息。当你的函数能抛出错误消息时,你应该在表达式中前置 try 关键词。do { try canThrowAnError() // 没有错误消息抛出} catch { // 有一个错误消息抛出}
一个 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(_:) 函数会被调用,并且使用 catch 所捕捉到的关联值 [String] 作为参数。
断言和先决条件
断言和先决条件是在运行时所做的检查。你可以用他们来检查在执行后续代码之前是否一个必要的条件已经被满足了。如果断言或者先决条件中的布尔条件评估的结果为 true(真),则代码像往常一样继续执行。如果布尔条件评估结果为 false(假),程序的当前状态是无效的,则代码执行结束,应用程序中止。断言和先决条件的不同点是,他们什么时候进行状态检测:断言仅在调试环境运行,而先决条件则在调试环境和生产环境中运行。在生产环境中,断言的条件将不会进行评估。这个意味着你可以使用很多断言在你的开发阶段,但是这些断言在生产环境中不会产生任何影响。
使用断言进行调试
如果代码已经检查了条件,你可以使用 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.\")}
强制执行先决条件
注意如果你使用 unchecked 模式(-Ounchecked)编译代码,先决条件将不会进行检查。编译器假设所有的先决条件总是为 true(真),他将优化你的代码。然而,fatalError(_:file:line:) 函数总是中断执行,无论你怎么进行优化设定。你能使用 fatalError(_:file:line:) 函数在设计原型和早期开发阶段,这个阶段只有方法的声明,但是没有具体实现,你可以在方法体中写上 fatalError(\"Unimplemented\")作为具体实现。因为 fatalError 不会像断言和先决条件那样被优化掉,所以你可以确保当代码执行到一个没有被实现的方法时,程序会被中断。
基本运算符
赋值运算符
运算符分为一元、二元和三元运算符:
一元运算符对单一操作对象操作(如 -a)。一元运算符分前置运算符和后置运算符,前置运算符需紧跟在操作对象之前(如 !b),后置运算符需紧跟在操作对象之后(如 c!)。
二元运算符操作两个操作对象(如 2 + 3),是中置的,因为它们出现在两个操作对象之间。
三元运算符操作三个操作对象,和 C 语言一样,Swift 只有一个三元运算符,就是三目运算符(a ? b : c)。
赋值运算符(a = b),表示用 b 的值来初始化或更新 a 的值:let b = 10var a = 5a = b// a 现在等于 10
算术运算符
加法(+)减法(-)乘法(*)除法(/)
加法运算符也可用于 String 的拼接:\
求余运算符
a = (b × 倍数) + 余数
9 % 4 // 等于 19 = (4 × 2) + 1
-9 % 4 // 等于 -1-9 = (4 × -2) + -1
一元负号运算符
一元正号运算符
一元正号符(+)不做任何改变地返回操作数的值:
let minusSix = -6let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6
组合赋值运算符
如同 C 语言,Swift 也提供把其他运算符和赋值运算(=)组合的组合赋值运算符,组合加运算(+=)是其中一个例子:var a = 1a += 2// a 现在是 3
注意复合赋值运算没有返回值,let b = a += 2 这类代码是错误。这不同于上面提到的自增和自减运算符。
比较运算符(Comparison Operators
Swift 支持以下的比较运算符:等于(a == b)不等于(a != b)大于(a > b)小于(a < b)大于等于(a >= b)小于等于(a <= b)
比较运算多用于条件语句,如 if 条件:let name = \"world\"if name == \"world\" { print(\
注意Swift 标准库只能比较七个以内元素的元组比较函数。如果你的元组元素超过七个时,你需要自己实现比较运算符。
三元运算符(Ternary Conditional Operator)
三元运算符的特殊在于它是有三个操作数的运算符,它的形式是 问题 ? 答案 1 : 答案 2。它简洁地表达根据 问题成立与否作出二选一的操作。如果 问题 成立,返回 答案 1 的结果;反之返回 答案 2 的结果。三元运算符是以下代码的缩写形式:if question { answer1} else { answer2}
let contentHeight = 40let hasHeader = truelet rowHeight = contentHeight + (hasHeader ? 50 : 20)// rowHeight 现在是 90
空合运算符(Nil Coalescing Operator)
空合运算符(a ?? b)将对可选类型 a 进行空判断,如果 a 包含一个值就进行解包,否则就返回一个默认值 b。表达式 a 必须是 Optional 类型。默认值 b 的类型必须要和 a 存储值的类型保持一致。空合运算符是对以下代码的简短表达方法:a != nil ? a! : b注意如果 a 为非空值(non-nil),那么值 b 将不会被计算。这也就是所谓的短路求值。
let defaultColorName = \"red\"var userDefinedColorName: String? //默认值为 nilvar colorNameToUse = userDefinedColorName ?? defaultColorName// userDefinedColorName 的值为空,所以 colorNameToUse 的值为 \"red\"
userDefinedColorName = \"green\"colorNameToUse = userDefinedColorName ?? defaultColorName// userDefinedColorName 非空,因此 colorNameToUse 的值为 \"green\"
区间运算符(Range Operators)
闭区间运算符
闭区间运算符(a...b)定义一个包含从 a 到 b(包括 a 和 b)的所有值的区间。a 的值不能超过 b。闭区间运算符在迭代一个区间的所有值时是非常有用的,如在 for-in 循环中:for index in 1...5 { print(\"\\(index) * 5 = \\(index * 5)\")}// 1 * 5 = 5// 2 * 5 = 10// 3 * 5 = 15// 4 * 5 = 20// 5 * 5 = 25
半开区间运算符
半开区间运算符(a..<b)定义一个从 a 到 b 但不包括 b 的区间。 之所以称为半开区间,是因为该区间包含第一个值而不包括最后的值。半开区间的实用性在于当你使用一个从 0 开始的列表(如数组)时,非常方便地从0数到列表的长度。let names = [\"Anna\
单侧区间
闭区间操作符有另一个表达形式,可以表达往一侧无限延伸的区间 —— 例如,一个包含了数组从索引 2 到结尾的所有值的区间。在这些情况下,你可以省略掉区间操作符一侧的值。这种区间叫做单侧区间,因为操作符只有一侧有值。例如:for name in names[2...] { print(name)}// Brian Jackfor name in names[...2] { print(name)}// Anna Alex Brianlet range = ...5range.contains(7) // false range.contains(4) // true range.contains(-1) // true
逻辑运算符(Logical Operators)
逻辑运算符的操作对象是逻辑布尔值。Swift 支持基于 C 语言的三个标准逻辑运算。逻辑非(!a) 逻辑与(a && b)逻辑或(a || b)
逻辑非运算符(!a)对一个布尔值取反,使得 true 变 false,false 变 true。let allowedEntry = falseif !allowedEntry { print(\"ACCESS DENIED\")}// 输出“ACCESS DENIED” if !allowedEntry 语句可以读作「如果非 allowedEntry」,接下一行代码只有在「非 allowedEntry」为 true,即 allowEntry 为 false 时被执行。
逻辑与运算符(a && b)表达了只有 a 和 b 的值都为 true 时,整个表达式的值才会是 true。只要任意一个值为 false,整个表达式的值就为 falselet enteredDoorCode = truelet passedRetinaScan = falseif enteredDoorCode && passedRetinaScan { print(\"Welcome!\")} else { print(\"ACCESS DENIED\")}// 输出“ACCESS DENIED”
逻辑或运算符(a || b)是一个由两个连续的 | 组成的中置运算符。它表示了两个逻辑表达式的其中一个为 true,整个表达式就为 true。let hasDoorKey = falselet knowsOverridePassword = trueif hasDoorKey || knowsOverridePassword { print(\"Welcome!\")} else { print(\"ACCESS DENIED\")}// 输出“Welcome!”
括号优先级
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword { print(\"Welcome!\")} else { print(\"ACCESS DENIED\")}// 输出“Welcome!”注意Swift 逻辑操作符 && 和 || 是左结合的,这意味着拥有多元逻辑操作符的复合表达式优先计算最左边的子表达式。
字符串和字符
字符串
与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。但 Swift 中的 String 类型的实现却很快速和现代化。每一个字符串都是由编码无关的 Unicode 字符组成,并支持访问字符的多种 Unicode 表示形式。注意Swift 的 String 类型与 Foundation NSString 类进行了无缝桥接。Foundation 还对 String 进行扩展使其可以访问 NSString 类型中定义的方法。这意味着调用那些 NSString 的方法,你无需进行任何类型转换。
字符串字面量
字符串字面量是由一对双引号包裹着的具有固定顺序的字符集。字符串字面量可以用于为常量和变量提供初始值:let someString = \"Some string literal value\"注意,Swift 之所以推断 someString 常量为字符串类型,是因为它使用了字面量方式进行初始化。
多行字符串字面量
一个多行字符串字面量包含了所有的在开启和关闭引号中的行。这个字符从开启引号(\"\"\")之后的第一行开始,到关闭引号(\"\"\")之前为止。这就意味着字符串开启引号之后(\"\"\")或者结束引号(\"\"\")之前都没有换行符号。
如果你的代码中,多行字符串字面量包含换行符的话,则多行字符串字面量中也会包含换行符。如果你想换行,以便加强代码的可读性,但是你又不想在你的多行字符串字面量中出现换行符的话,你可以用在行尾写一个反斜杠(\\)作为续行符。let softWrappedQuotation = \"\"\"The White Rabbit put on his spectacles. \
为了让一个多行字符串字面量开始和结束于换行符,请将换行写在第一行和最后一行,例如:let lineBreaks = \"\"\"This string starts with a line break.It also ends with a line break.\"\"\"
字符串字面量的特殊字符
字符串字面量可以包含以下特殊字符:转义字符 \\0(空字符)、\\\\(反斜线)、\\t(水平制表符)、\(换行符)、\(回车符)、\\\"(双引号)、\\'(单引号)。Unicode 标量,写成 \\u{n}(u 为小写),其中 n 为任意一到八位十六进制数且可用的 Unicode 位码。let wiseWords = \"\\\"Imagination is more important than knowledge\\\" - Einstein\"// \"Imageination is more important than knowledge\" - Enisteinlet dollarSign = \"\\u{24}\" // $,Unicode 标量 U+0024let blackHeart = \"\\u{2665}\" // ♥,Unicode 标量 U+2665let sparklingHeart = \"\\u{1F496}\" // 💖,Unicode 标量 U+1F496
由于多行字符串字面量使用了三个双引号,而不是一个,所以你可以在多行字符串字面量里直接使用双引号(\")而不必加上转义符(\\)。要在多行字符串字面量中使用 \"\"\" 的话,就需要使用至少一个转义符(\\):let threeDoubleQuotes = \"\"\"Escaping the first quote \\\"\"\"Escaping all three quotes \\\"\\\"\\\"\"\"\"
扩展字符串分隔符
您可以将字符串文字放在扩展分隔符中,这样字符串中的特殊字符将会被直接包含而非转义后的效果。将字符串放在引号(\")中并用数字符号(#)括起来。例如,打印字符串文字 #\"Line 1 \Line 2\"# 会打印换行符转义序列(\)而不是给文字换行。如果需要字符串文字中字符的特殊效果,请匹配转义字符(\\)后面添加与起始位置个数相匹配的 # 符。 例如,如果您的字符串是 #\"Line 1 \Line 2\"# 并且您想要换行,则可以使用 #\"Line 1 \\#nLine 2\"# 来代替。 同样,###\"Line1 \\###nLine2\"### 也可以实现换行效果。
可以使用扩展分隔符在多行字符串中包含文本 \"\"\",覆盖原有的结束文字的默认行为。例如:let threeMoreDoubleQuotationMarks = #\"\"\"Here are three more double quotes: \"\"\"\"\"\"#
初始化空字符串
var emptyString = \"\" // 空字符串字面量var anotherEmptyString = String() // 初始化方法// 两个字符串均为空并等价。
可以通过检查 Bool 类型的 isEmpty 属性来判断该字符串是否为空:if emptyString.isEmpty { print(\"Nothing to see here\")}// 打印输出:“Nothing to see here”
字符串可变性
可以通过将一个特定字符串分配给一个变量来对其进行修改,或者分配给一个常量来保证其不会被修改:var variableString = \"Horse\"variableString += \" and carriage\"// variableString 现在为 \"Horse and carriage\"let constantString = \"Highlander\"constantString += \" and another Highlander\"// 这会报告一个编译错误(compile-time error) - 常量字符串不可以被修改。
字符串是值类型
在 Swift 中 String 类型是值类型。如果你创建了一个新的字符串,那么当其进行常量、变量赋值操作,或在函数/方法中传递时,会进行值拷贝。Swift 默认拷贝字符串的行为保证了在函数/方法向你传递的字符串所属权属于你,无论该值来自于哪里。你可以确信传递的字符串不会被修改,除非你自己去修改它。在实际编译时,Swift 编译器会优化字符串的使用,使实际的复制只发生在绝对必要的情况下,这意味着你将字符串作为值类型的同时可以获得极高的性能。
for character in \"Dog!🐶\" { print(character)}// D// o// g// !// 🐶
字符串可以通过传递一个值类型为 Character 的数组作为自变量来初始化:
let catCharacters: [Character] = [\"C\
连接字符串和字符
可以通过加法赋值运算符(+=)将一个字符串添加到一个已经存在字符串变量上:
let string2 = \" there\"var instruction = \"look over\"instruction += string2// instruction 现在等于 \"look over there\"
可以用 append() 方法将一个字符附加到一个字符串变量的尾部:
let exclamationMark: Character = \"!\"welcome.append(exclamationMark)// welcome 现在等于 \"hello there!\"
注意你不能将一个字符串或者字符添加到一个已经存在的字符变量上,因为字符变量只能包含一个字符。
如果你需要使用多行字符串字面量来拼接字符串,并且你需要字符串每一行都以换行符结尾,包括最后一行:
let badStart = \"\"\"onetwo\"\"\"let end = \"\"\"three\"\"\"print(badStart + end)// 打印两行:// one// twothreelet goodStart = \"\"\"onetwo\"\"\"print(goodStart + end)// 打印三行:// one// two// three
字符串插值
字符串字面量和多行字符串字面量都可以使用字符串插值。你插入的字符串字面量的每一项都在以反斜线为前缀的圆括号中:let multiplier = 3let message = \"\\(multiplier) times 2.5 is \\(Double(multiplier) * 2.5)\"// message 是 \"3 times 2.5 is 7.5\"
如果要在使用扩展字符串分隔符的字符串中使用字符串插值,需要在反斜杠后面添加与开头和结尾数量相同扩展字符串分隔符。例如:print(#\"6 times 7 is \\#(6 * 7).\"#)// 打印 \"6 times 7 is 42.\"
注意插值字符串中写在括号中的表达式不能包含非转义反斜杠(\\),并且不能包含回车或换行符。不过,插值字符串可以包含其他字面量
Unicode
Swift 的 String 类型是基于 Unicode 标量 建立的。Unicode 标量是对应字符或者修饰符的唯一的 21 位数字,能够以其他三种 Unicode 兼容的方式访问字符串的值:UTF-8 代码单元集合(利用字符串的 utf8 属性进行访问)UTF-16 代码单元集合(利用字符串的 utf16 属性进行访问)21 位的 Unicode 标量值集合,也就是字符串的 UTF-32 编码格式(利用字符串的 unicodeScalars 属性进行访问)
let dogString = \"Dog‼🐶\"for codeUnit in dogString.utf8 {print(\"\\(codeUnit) \
for codeUnit in dogString.utf16 {print(\"\\(codeUnit) \
for scalar in dogString.unicodeScalars {print(\"\\(scalar.value) \
for scalar in dogString.unicodeScalars { print(\"\\(scalar) \")}// D// o// g// ‼// 🐶
计算字符数量
如果想要获得一个字符串中 Character 值的数量,可以使用 count 属性:let unusualMenagerie = \
字符串索引
let greeting = \"Guten Tag!\
注意你可以使用 startIndex 和 endIndex 属性或者 index(before:) 、index(after:) 和 index(_:offsetBy:) 方法在任意一个确认的并遵循 Collection 协议的类型里面
插入和删除
调用 insert(_:at:) 方法可以在一个字符串的指定索引插入一个字符,调用 insert(contentsOf:at:) 方法可以在一个字符串的指定索引插入一个段字符串。var welcome = \"hello\"welcome.insert(\"!\
调用 remove(at:) 方法可以在一个字符串的指定索引删除一个字符,调用 removeSubrange(_:) 方法可以在一个字符串的指定索引删除一个子字符串。welcome.remove(at: welcome.index(before: welcome.endIndex))// welcome 现在等于 \"hello there\
注意你可以使用 insert(_:at:)、insert(contentsOf:at:)、remove(at:) 和 removeSubrange(_:) 方法在任意一个确认的并遵循 RangeReplaceableCollection 协议的类型里面,如上文所示是使用在 String 中,你也可以使用在 Array、Dictionary 和 Set 中。
你从字符串中获取一个子字符串 —— 例如,使用下标或者 prefix(_:) 之类的方法 —— 就可以得到一个 Substring 的实例,而非另外一个 String。当你需要长时间保存结果时,就把 Substring 转化为 String 的实例:let greeting = \
比较字符串
字符串/字符相等
字符串/字符可以用等于操作符(==)和不等于操作符(!=),详细描述在 比较运算符:let quotation = \
前缀/后缀相等
通过调用字符串的 hasPrefix(_:)/hasSuffix(_:) 方法来检查字符串是否拥有特定前缀/后缀,两个方法均接收一个 String 类型的参数,并返回一个布尔值。hasPrefix(_:) 和 hasSuffix(_:) 方法都是在每个字符串中逐字符比较其可扩展的字符群集是否标准相等let romeoAndJuliet = [ \
集合类型
数组(Arrays)
Swift 的 Array 类型被桥接到 Foundation 中的 NSArray 类。Swift 中数组的完整写法为 Array<Element>,其中 Element 是这个数组中唯一允许存在的数据类型。也可以使用像 [Element] 这样的简单语法。
创建一个空数组: var someInts = [Int]()print(\"someInts is of type [Int] with \\(someInts.count) items.\")// 打印“someInts is of type [Int] with 0 items.”如果代码上下文中已经提供了类型信息,例如一个函数参数或者一个已经定义好类型的常量或者变量,你可以使用空数组语句创建一个空数组,它的写法很简单:[]someInts.append(3)// someInts 现在包含一个 Int 值someInts = []// someInts 现在是空数组,但是仍然是 [Int] 类型的。
创建一个带有默认值的数组通过两个数组相加创建一个数组
用数组字面量构造数组
var shoppingList: [String] = [\"Eggs\
注意shoppingList 数组被声明为变量(var 关键字创建)而不是常量(let 创建)是因为之后会有更多的数据项被插入其中。
访问和修改数组
使用布尔属性 isEmpty 作为一个缩写形式去检查 count 属性是否为 0:if shoppingList.isEmpty { print(\"The shopping list is empty.\")} else { print(\"The shopping list is not empty.\")}// 打印“The shopping list is not empty.”(shoppinglist 不是空的)
可以使用 append(_:) 方法在数组后面添加新的数据项:shoppingList.append(\"Flour\")// shoppingList 现在有3个数据项
除此之外,也可以使用加法赋值运算符(+=)直接将另一个相同类型数组中的数据添加到该数组后面:shoppingList += [\"Baking Powder\"]// shoppingList 现在有四项了shoppingList += [\"Chocolate Spread\
var firstItem = shoppingList[0]// 第一项是“Eggs”
以利用下标来一次改变一系列数据值,即使新数据和原有数据的数量是不一样的。下面的例子把 \"Chocolate Spread\"、\"Cheese\" 和 \"Butter\" 替换为 \"Bananas\" 和 \"Apples\":shoppingList[4...6] = [\"Bananas\
通过调用数组的 insert(_:at:) 方法在某个指定索引值之前添加数据项:shoppingList.insert(\"Maple Syrup\
let mapleSyrup = shoppingList.remove(at: 0)// 索引值为0的数据项被移除// shoppingList 现在只有6项,而且不包括 Maple Syrup// mapleSyrup 常量的值等于被移除数据项“Maple Syrup”
Swift 中的数组索引总是从零开始。除了当 count 等于 0 时(说明这是个空数组),最大索引值一直是 count - 1,因为数组都是零起索引。
如果你只想把数组中的最后一项移除,可以使用 removeLast() 方法而不是 remove(at:) 方法来避免需要获取数组的 count 属性。就像后者一样,前者也会返回被移除的数据项:let apples = shoppingList.removeLast()// 数组的最后一项被移除了// shoppingList 现在只有5项,不包括 Apples// apples 常量的值现在等于字符串“Apples”
数组的遍历
字典(Dictionary)
Swift 的 Dictionary 类型被桥接到 Foundation 的 NSDictionary 类字典是一种无序的集合,它存储的是键值对之间的关系,其所有键的值需要是相同的类型,所有值的类型也需要相同。每个值(value)都关联唯一的键(key),键作为字典中这个值数据的标识符。和数组中的数据项不同,字典中的数据项并没有具体顺序。
创建一个空字典
var namesOfIntegers = [Int: String]()// namesOfIntegers 是一个空的 [Int: String] 字典
如果上下文已经提供了类型信息,你可以使用空字典字面量来创建一个空字典,记作 [:] (一对方括号中放一个冒号)namesOfIntegers[16] = \"sixteen\"// namesOfIntegers 现在包含一个键值对namesOfIntegers = [:]// namesOfIntegers 又成为了一个 [Int: String] 类型的空字典
用字典字面量创建字典
var airports: [String: String] = [\"YYZ\": \"Toronto Pearson\
访问和修改字典
和数组一样,可以通过 Dictionary 的只读属性 count 来获取字典的数据项数量:print(\"The dictionary of airports contains \\(airports.count) items.\")// 打印“The dictionary of airports contains 2 items.”(这个字典有两个数据项)
使用布尔属性 isEmpty 作为一个缩写形式去检查 count 属性是否为 0:if airports.isEmpty { print(\"The airports dictionary is empty.\")} else { print(\"The airports dictionary is not empty.\")}// 打印“The airports dictionary is not empty.”
你可以通过下标语法来给字典添加新的数据项。可以使用一个恰当类型的键作为下标索引,并且分配恰当类型的新值:airports[\"LHR\"] = \"London\"// airports 字典现在有三个数据项
也可以使用下标语法来改变特定键对应的值airports[\"LHR\"] = \"London Heathrow\"// “LHR”对应的值被改为“London Heathrow”
updateValue(_:forKey:) 方法会返回对应值类型的可选类型。举例来说:对于存储 String 值的字典,这个函数会返回一个 String? 或者“可选 String”类型的值。如果有值存在于更新前,则这个可选值包含了旧值,否则它将会是 nil :if let oldValue = airports.updateValue(\"Dublin Airport\
还可以使用下标语法通过将某个键的对应值赋值为 nil 来从字典里移除一个键值对:airports[\"APL\"] = \"Apple Internation\"// “Apple Internation”不是真的 APL 机场,删除它airports[\"APL\"] = nil// APL 现在被移除了removeValue(forKey:) 方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的值或者在没有对应值的情况下返回 nil:if let removedValue = airports.removeValue(forKey: \"DUB\") { print(\"The removed airport's name is \\(removedValue).\")} else { print(\"The airports dictionary does not contain a value for DUB.\")}// 打印“The removed airport's name is Dublin Airport.”
字典遍历
for airportCode in airports.keys { print(\"Airport code: \\(airportCode)\")}// Airport code: YYZ// Airport code: LHRfor airportName in airports.values { print(\"Airport name: \\(airportName)\")}// Airport name: Toronto Pearson// Airport name: London Heathrow
let airportCodes = [String](airports.keys)// airportCodes 是 [\"YYZ\
Swift 的 Dictionary 是无序集合类型。为了以特定的顺序遍历字典的键或值,可以对字典的 keys 或 values 属性使用 sorted() 方法。
集合(Set)
注意 Swift 的 Set 类型被桥接到 Foundation 中的 NSSet 类。集合用来存储相同类型并且没有确定顺序的值。当集合元素顺序不重要时或者希望确保每个元素只出现一次时可以使用集合而不是数组。注意 Swift 的 Set 类型被桥接到 Foundation 中的 NSSet 类。Swift 的所有基本类型(比如 String、Int、Double 和 Bool)默认都是可哈希化的,可以作为集合值的类型或者字典键的类型。没有关联值的枚举成员值(在 枚举 有讲述)默认也是可哈希化的。
Swift 中的集合类型被写为 Set<Element>,这里的 Element 表示集合中允许存储的类型。和数组不同的是,集合没有等价的简化形式。
创建和构造一个空的集合
var letters = Set<Character>()print(\"letters is of type Set<Character> with \\(letters.count) items.\")// 打印“letters is of type Set<Character> with 0 items.”此外,如果上下文提供了类型信息,比如作为函数的参数或者已知类型的变量或常量,你可以通过一个空的数组字面量创建一个空的集合:letters.insert(\"a\")// letters 现在含有1个 Character 类型的值letters = []// letters 现在是一个空的 Set,但是它依然是 Set<Character> 类型
用数组字面量创建集合
var favoriteGenres: Set<String> = [\"Rock\
访问和修改一个集合
print(\"I have \\(favoriteGenres.count) favorite music genres.\")// 打印“I have 3 favorite music genres.”if favoriteGenres.isEmpty { print(\
if let removedGenre = favoriteGenres.remove(\"Rock\") { print(\"\\(removedGenre)? I'm over it.\")} else { print(\"I never much cared for that.\")}// 打印“Rock? I'm over it.”
使用 contains(_:) 方法去检查集合中是否包含一个特定的值:if favoriteGenres.contains(\"Funk\") { print(\"I get up on the good foot.\")} else { print(\"It's too funky in here.\")}// 打印“It's too funky in here.”
遍历一个集合
for genre in favoriteGenres { print(\"\\(genre)\")}// Classical// Jazz// Hip hop
Swift 的 Set 类型没有确定的顺序,为了按照特定顺序来遍历一个集合中的值可以使用 sorted() 方法,它将返回一个有序数组,这个数组的元素排列顺序由操作符 < 对元素进行比较的结果来确定。
for genre in favoriteGenres.sorted() { print(\"\\(genre)\")}// Classical// Hip hop// Jazz
集合操作
集合成员关系和相等
使用“是否相等”运算符(==)来判断两个集合包含的值是否全部相同。使用 isSubset(of:) 方法来判断一个集合中的所有值是否也被包含在另外一个集合中。使用 isSuperset(of:) 方法来判断一个集合是否包含另一个集合中所有的值。使用 isStrictSubset(of:) 或者 isStrictSuperset(of:) 方法来判断一个集合是否是另外一个集合的子集合或者父集合并且两个集合并不相等。使用 isDisjoint(with:) 方法来判断两个集合是否不含有相同的值(是否没有交集)。let houseAnimals: Set = [\"🐶\
Swift 语言提供数组(Array)、集合(Set)和字典(Dictionary)三种基本的集合类型用来存储集合数据。数组是有序数据的集。集合是无序无重复数据的集。字典是无序的键值对的集。
流程控制
For-In 循环
let numberOfLegs = [\"spider\
for index in 1...5 { print(\"\\(index) times 5 is \\(index * 5)\")}// 1 times 5 is 5// 2 times 5 is 10// 3 times 5 is 15// 4 times 5 is 20// 5 times 5 is 25
如果你不需要区间序列内每一项的值,你可以使用下划线(_)替代变量名来忽略这个值:let base = 3let power = 10var answer = 1for _ in 1...power { answer *= base}print(\"\\(base) to the power of \\(power) is \\(answer)\")// 输出“3 to the power of 10 is 59049”
let minutes = 60for tickMark in 0..<minutes { // 每一分钟都渲染一个刻度线(60次)}
while循环
while 循环会一直运行一段语句直到条件变成 false。这类循环适合使用在第一次迭代前,迭代次数未知的情况下。Swift 提供两种 while 循环形式:while 循环,每次在循环开始时计算条件是否符合;repeat-while 循环,每次在循环结束时计算条件是否符合。
While
while 循环从计算一个条件开始。如果条件为 true,会重复运行一段语句,直到条件变为 false。一般格式:while condition { statements}
Repeat-While
while 循环的另外一种形式是 repeat-while,它和 while 的区别是在判断循环条件之前,先执行一次循环的代码块。然后重复循环直到条件为 false。注意Swift 语言的 repeat-while 循环和其他语言中的 do-while 循环是类似的。repeat-while 循环的一般格式:repeat { statements} while condition
条件语句
Swift 提供两种类型的条件语句:if 语句和 switch 语句。通常,当条件较为简单且可能的情况很少时,使用 if 语句。而 switch 语句更适用于条件较复杂、有更多排列组合的时候。并且 switch 在需要用到模式匹配(pattern-matching)的情况下会更有用。
temperatureInFahrenheit = 90if temperatureInFahrenheit <= 32 { print(\"It's very cold. Consider wearing a scarf.\")} else if temperatureInFahrenheit >= 86 { print(\"It's really warm. Don't forget to wear sunscreen.\")} else { print(\"It's not that cold. Wear a t-shirt.\")}// 输出“It's really warm. Don't forget to wear sunscreen.”
区间匹配
let approximateCount = 62let countedThings = \"moons orbiting Saturn\"let naturalCount: Stringswitch approximateCount {case 0: naturalCount = \"no\"case 1..<5: naturalCount = \"a few\"case 5..<12: naturalCount = \"several\"case 12..<100: naturalCount = \"dozens of\"case 100..<1000: naturalCount = \"hundreds of\"default: naturalCount = \"many\"}print(\"There are \\(naturalCount) \\(countedThings).\")// 输出“There are dozens of moons orbiting Saturn.”
元组
Where
let someCharacter: Character = \"e\"switch someCharacter {case \"a\
控制转移语句
控制转移语句改变你代码的执行顺序,通过它可以实现代码的跳转。Swift 有五种控制转移语句:continuebreakfallthroughreturnthrow
Continue
continue 语句告诉一个循环体立刻停止本次循环,重新开始下次循环。就好像在说“本次循环我已经执行完了”,但是并不会离开整个循环体。let puzzleInput = \"great minds think alike\"var puzzleOutput = \"\"for character in puzzleInput { switch character { case \"a\
break
break 语句会立刻结束整个控制流的执行。break 可以在 switch 或循环语句中使用,用来提前结束 switch 或循环语句。let numberSymbol: Character = \"三\" // 简体中文里的数字 3var possibleIntegerValue: Int?switch numberSymbol {case \"1\
贯穿(Fallthrough)
注意fallthrough 关键字不会检查它下一个将会落入执行的 case 中的匹配条件。fallthrough 简单地使代码继续连接到下一个 case 中的代码,这和 C 语言标准中的 switch 语句特性是一样的。
使用 fallthrough 关键字来“贯穿”到 default 分支中。let integerToDescribe = 5var description = \"The number \\(integerToDescribe) is\
带标签的语句
可以使用标签(statement label)来标记一个循环体或者条件语句,对于一个条件语句,你可以使用 break 加标签的方式,来结束这个被标记的语句。对于一个循环语句,你可以使用 break 或者 continue 加标签,来结束或者继续这条被标记语句的执行。声明一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签,作为这个语句的前导关键字(introducor keyword),并且该标签后面跟随一个冒号。下面是一个针对 while 循环体的标签语法,同样的规则适用于所有的循环体和条件语句。 label name: while condition { statements }
注意如果上述的 break 语句没有使用 gameLoop 标签,那么它将会中断 switch 语句而不是 while 循环。使用 gameLoop 标签清晰的表明了 break 想要中断的是哪个代码块。同时请注意,当调用 continue gameLoop 去跳转到下一次循环迭代时,这里使用 gameLoop 标签并不是严格必须的。因为在这个游戏中,只有一个循环体,所以 continue 语句会影响到哪个循环体是没有歧义的。然而,continue 语句使用 gameLoop 标签也是没有危害的。这样做符合标签的使用规则,同时参照旁边的 break gameLoop,能够使游戏的逻辑更加清晰和易于理解。
guard 提前退出
像 if 语句一样,guard 的执行取决于一个表达式的布尔值。我们可以使用 guard 语句来要求条件必须为真时,以执行 guard 语句后的代码。不同于 if 语句,一个 guard 语句总是有一个 else 从句,如果条件不为真则执行 else 从句中的代码。
func greet(person: [String: String]) { guard let name = person[\"name\"] else { return } print(\"Hello \\(name)!\") guard let location = person[\"location\"] else { print(\"I hope the weather is nice near you.\") return } print(\"I hope the weather is nice in \\(location).\")}greet(person: [\"name\": \"John\"])// 输出“Hello John!”// 输出“I hope the weather is nice near you.”greet(person: [\"name\": \"Jane\
如果 guard 语句的条件被满足,则继续执行 guard 语句大括号后的代码。将变量或者常量的可选绑定作为 guard 语句的条件,都可以保护 guard 语句后面的代码。如果条件不被满足,在 else 分支上的代码就会被执行。这个分支必须转移控制以退出 guard 语句出现的代码段。它可以用控制转移语句如 return、break、continue 或者 throw 做这件事,或者调用一个不返回的方法或函数,例如 fatalError()。相比于可以实现同样功能的 if 语句,按需使用 guard 语句会提升我们代码的可读性。它可以使你的代码连贯的被执行而不需要将它包在 else 块中,它可以使你在紧邻条件判断的地方,处理违规的情况。
函数
函数的定义与调用
当你定义一个函数时,你可以定义一个或多个有名字和类型的值,作为函数的输入,称为参数,也可以定义某种类型的值作为函数执行结束时的输出,称为返回类型。func greetAgain(person: String) -> String { return \
函数参数与返回值
无参数函数
func sayHelloWorld() -> String { return \
多参数函数
无返回值函数
func greet(person: String) { print(\
这个函数不需要返回值,所以这个函数的定义中没有返回箭头(->)和返回类型。注意严格地说,即使没有明确定义返回值,该 greet(Person:) 函数仍然返回一个值。没有明确定义返回类型的函数的返回一个 Void 类型特殊值,该值为一个空元组,写成 ()。
调用函数时,可以忽略该函数的返回值:func printAndCount(string: String) -> Int { print(string) return string.count}func printWithoutCounting(string: String) { let _ = printAndCount(string: string)}printAndCount(string: \
第一个函数 printAndCount(string:),输出一个字符串并返回 Int 类型的字符数。第二个函数 printWithoutCounting(string:) 调用了第一个函数,但是忽略了它的返回值。当第二个函数被调用时,消息依然会由第一个函数输出,但是返回值不会被用到。注意返回值可以被忽略,但定义了有返回值的函数必须返回一个值,如果在函数定义底部没有返回任何值,将导致编译时错误。
多重返回值函数
可选元组返回类型
隐式返回的函数
如果一个函数的整个函数体是一个单行表达式,这个函数可以隐式地返回这个表达式。举个例子,以下的函数有着同样的作用:func greeting(for person: String) -> String { \
greeting(for:) 函数的完整定义是打招呼内容的返回,这就意味着它能使用隐式返回这样更简短的形式。anothergreeting(for:) 函数返回同样的内容,却因为 return 关键字显得函数更长。任何一个可以被写成一行 return 语句的函数都可以忽略 return。
函数参数标签和参数名称
指定参数标签
你可以在参数名称前指定它的参数标签,中间以空格分隔:func someFunction(argumentLabel parameterName: Int) { // 在函数体内,parameterName 代表参数值}
忽略参数标签
默认参数值
将不带有默认值的参数放在函数参数列表的最前。一般来说,没有默认值的参数更加的重要,将不带默认值的参数放在最前保证在函数调用时,非默认参数的顺序是一致的,同时也使得相同的函数在不同情况下调用时显得更为清晰。
可变参数
一个可变参数(variadic parameter)可以接受零个或多个值。函数调用时,你可以用可变参数来指定函数参数可以被传入不确定数量的输入值。通过在变量类型名后面加入(...)的方式来定义可变参数。可变参数的传入值在函数体中变为此类型的一个数组。例如,一个叫做 numbers 的 Double... 型可变参数,在函数体内可以当做一个叫 numbers 的 [Double] 型的数组常量。
注意一个函数最多只能拥有一个可变参数。
输入输出参数
如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为输入输出参数(In-Out Parameters)。定义一个输入输出参数时,在参数定义前加 inout 关键字。一个 输入输出参数有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。
只能传递变量给输入输出参数。你不能传入常量或者字面量,因为这些量是不能被修改的。当传入的参数作为输入输出参数时,需要在参数名前加 & 符,表示这个值可以被函数修改。注意输入输出参数不能有默认值,而且可变参数不能用 inout 标记。
注意输入输出参数和返回值是不一样的。上面的 swapTwoInts 函数并没有定义任何返回值,但仍然修改了 someInt 和 anotherInt 的值。输入输出参数是函数对函数体外产生影响的另一种方式。
函数类型
每个函数都有种特定的函数类型,函数的类型由函数的参数类型和返回类型组成。
下面是另一个例子,一个没有参数,也没有返回值的函数:func printHelloWorld() { print(\
使用函数类型
print(\
mathFunction = multiplyTwoIntsprint(\
函数类型作为参数类型
函数类型作为返回类型
你可以用函数类型作为另一个函数的返回类型。你需要做的是在返回箭头(->)后写一个完整的函数类型。
func stepForward(_ input: Int) -> Int { return input + 1}func stepBackward(_ input: Int) -> Int { return input - 1}func chooseStepFunction(backward: Bool) -> (Int) -> Int { return backward ? stepBackward : stepForward}var currentValue = 3let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)// moveNearerToZero 现在指向 stepBackward() 函数。print(\"Counting to zero:\")// Counting to zero:while currentValue != 0 { print(\"\\(currentValue)... \") currentValue = moveNearerToZero(currentValue)}print(\"zero!\")// 3...// 2...// 1...// zero!
上面这个例子中计算出从 currentValue 逐渐接近到0是需要向正数走还是向负数走。currentValue 的初始值是 3,这意味着 currentValue > 0 为真(true),这将使得 chooseStepFunction(_:) 返回 stepBackward(_:) 函数。一个指向返回的函数的引用保存在了 moveNearerToZero 常量中。现在,moveNearerToZero 指向了正确的函数,它可以被用来数到零:
嵌套函数
你所见到的所有函数都叫全局函数(global functions),它们定义在全局域中。你也可以把函数定义在别的函数体中,称作 嵌套函数(nested functions)。默认情况下,嵌套函数是对外界不可见的,但是可以被它们的外围函数(enclosing function)调用。一个外围函数也可以返回它的某一个嵌套函数,使得这个函数可以在其他域中被使用。
你可以用返回嵌套函数的方式重写 chooseStepFunction(backward:) 函数:func chooseStepFunction(backward: Bool) -> (Int) -> Int { func stepForward(input: Int) -> Int { return input + 1 } func stepBackward(input: Int) -> Int { return input - 1 } return backward ? stepBackward : stepForward}var currentValue = -4let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)// moveNearerToZero now refers to the nested stepForward() functionwhile currentValue != 0 { print(\"\\(currentValue)... \") currentValue = moveNearerToZero(currentValue)}print(\"zero!\")// -4...// -3...// -2...// -1...// zero!
闭包
Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数(Lambdas)比较相似。闭包可以捕获和存储其所在上下文中任意常量和变量的引用。被称为包裹常量和变量。 Swift 会为你管理在捕获过程中涉及到的所有内存操作。在 函数 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采用如下三种形式之一:全局函数是一个有名字但不会捕获任何值的闭包嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包
Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进行语法优化,主要优化如下:利用上下文推断参数和返回值类型隐式返回单表达式闭包,即单表达式闭包可以省略 return 关键字参数名称缩写尾随闭包语法
闭包表达式
排序方法
下面的闭包表达式示例使用 sorted(by:) 方法对一个 String 类型的数组进行字母逆序排序。以下是初始数组:let names = [\"Chris\
闭包表达式语法
根据上下文推断类型
实际上,通过内联闭包表达式构造的闭包作为参数传递给函数或方法时,总是能够推断出闭包的参数和返回值类型。这意味着闭包作为函数或者方法的参数时,你几乎不需要利用完整格式构造内联闭包。尽管如此,你仍然可以明确写出有着完整格式的闭包。如果完整格式的闭包能够提高代码的可读性,则我们更鼓励采用完整格式的闭包。而在 sorted(by:) 方法这个例子里,显然闭包的目的就是排序。由于这个闭包是为了处理字符串数组的排序,因此读者能够推测出这个闭包是用于字符串处理的。
单表达式闭包的隐式返回
参数名称缩写
Swift 自动为内联闭包提供了参数名称缩写功能,你可以直接通过 $0,$1,$2 来顺序调用闭包的参数,以此类推。如果你在闭包表达式中使用参数名称缩写,你可以在闭包定义中省略参数列表,并且对应参数名称缩写的类型会通过函数类型进行推断。in 关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:reversedNames = names.sorted(by: { $0 > $1 } )在这个例子中,$0 和 $1 表示闭包中第一个和第二个 String 类型的参数。
运算符方法
Swift 的 String 类型定义了关于大于号(>)的字符串实现,其作为一个函数接受两个 String 类型的参数并返回 Bool 类型的值。而这正好与 sorted(by:) 方法的参数需要的函数类型相符合。因此,你可以简单地传递一个大于号,Swift 可以自动推断找到系统自带的那个字符串函数的实现:reversedNames = names.sorted(by: >)
尾随闭包
如果你需要将一个很长的闭包表达式作为最后一个参数传递给函数,将这个闭包替换成为尾随闭包的形式很有用。尾随闭包是一个书写在函数圆括号之后的闭包表达式,函数支持将其作为最后一个参数调用。在使用尾随闭包时,你不用写出它的参数标签:
func someFunctionThatTakesAClosure(closure: () -> Void) { // 函数体部分}// 以下是不使用尾随闭包进行函数调用someFunctionThatTakesAClosure(closure: { // 闭包主体部分})// 以下是使用尾随闭包进行函数调用someFunctionThatTakesAClosure() { // 闭包主体部分}
在 闭包表达式语法 上章节中的字符串排序闭包可以作为尾随包的形式改写在 sorted(by:) 方法圆括号的外面:reversedNames = names.sorted() { $0 > $1 }
如果闭包表达式是函数或方法的唯一参数,则当你使用尾随闭包时,你甚至可以把 () 省略掉:reversedNames = names.sorted { $0 > $1 }
值捕获(capturing)
闭包是引用类型
逃逸闭包
自动闭包
枚举
枚举语法
使用 Switch 语句匹配枚举值
枚举成员的遍历
关联值
原始值
原始值的隐式赋值
使用原始值初始化枚举实例
递归枚举
类和结构体
结构体和类对比
类型定义的语法
结构体和类的实例
属性访问
结构体类型的成员逐一构造器
结构体和枚举是值类型
类是引用类型
恒等运算符
指针
属性
存储属性
常量结构体实例的存储属性
延时加载存储属性
存储属性和实例变量
计算属性
简化 Setter 声明
简化 Getter 声明
只读计算属性
属性观察器
属性包装器
设置被包装属性的初始值
从属性包装器中呈现一个值
全局变量和局部变量
类型属性
类型属性语法
获取和设置类型属性的值
方法
实例方法(Instance Methods)
self 属性
在实例方法中修改值类型
在可变方法中给 self 赋值
类型方法
下标
下标语法
下标用法
下标选项
类型下标
继承
定义一个基类
子类生成
重写
访问超类的方法,属性及下标
重写方法
重写属性
防止重写
子主题
0 条评论
回复 删除
下一页