注册 登录
主题 : Swift 程序设计指南
级别: *
UID: 213284
精华: *
发帖: *
可可豆: * CB
威望: * 点
在线时间: (时)
注册时间: *
最后登录: *
0 楼:  发表于: 2014-06-05 08:28    发自: Web Page
来源于 Swift教程 分类

Swift 程序设计指南   

管理提醒: 本帖被 阿花君霸占路人 执行加亮操作(2014-06-05)
    刘一道:各位兄弟姐妹,后续的章节,在持续的翻译之中。有愿意的翻译的,可在后面跟帖。
1基础知识

虽说 Swift 是开发 iOS OS X 应用的一门新编程语言,但它的开发体验与 C Objective-C 有很多相似之处。
Swift 重新实现了 C Objective-C 中的所有基础类型,包括表示整数的 Int,表示浮点数的 Double Float,表示布尔值的 Bool,以及表示纯文本数据的 String Swift 还为两个基本集合类型 Array Dictionary 提供了强大的支持,详情可参考 集合类型。
C 语言类似,Swift 也采用变量存储数据,并通过标识符来引用变量值。 Swift 还扩充了值不可变的量——常量的用法,使它比 C 语言中的常量强大得多。 当在 Swift 中操作不需要改变值的数据时,使用常量可使代码更安全、更简洁。
除常见类型以外,Swift 还引入了Objective-C 中不存在的高级类型,其中包括元组(tuple),可以新建或传递一组值。函数可以将多个值作为整体(单个元组值)返回给调用方。
Swift 还引入了可选量,可处理不存在的值。可选量可以“存在一个值 x”,也可以“不存在任何值”。可选量与 Objective-C 为指针赋 nil 相似,但在 Swift 中可以对任意类型使用,而不只针对类。可选量比 Objective-C nil 指针更安全且语义更丰富,在 Swift 最强大的诸多功能中得到了深入的应用。
可选量是 Swift 类型安全的一点体现。Swift 可帮助你清晰地了解代码能处理的数据类型。如果代码希望得到 String 数据,类型安全的特性将阻止你偶然将 Int 传递过去。这样可以在开发过程中尽可能早地发现与修正问题。
1.1 常量与变量
常量及变量将名称(如maximumNumberOfLoginAttempts welcomeMessage) 与特定类型的值(如数字 10 或字符串"Hello")关联起来。常量一旦赋值,其值不可再改变;而变量以后还可以改赋不同的值。
1. 常量及变量的声明
常量与变量在使用之前必须声明。使用 let 关键词声明常量,使用 var关键词声明变量。 下面是可以跟踪用户登录次数的常量与变量的例子:
let maximumNumberOfLoginAttempts = 10 // 允许尝试登录的次数
var currentLoginAttempt = 0          // 已经尝试登录的次数

这段代码可以这样理解:
“声明一个新的常量,其名称为 maximumNumberOfLoginAttempts,并将其赋值为 10 然后,声明一个新的变量,其名称为 currentLoginAttempt,并赋初始值为 0。”
在本例中,允许尝试的最多登录次数作为常量声明,因为允许的次数在执行时永远不会发生变化。当前已尝试次数的计数器作为变量声明,因为该值必须在登录失败时递增。
可以在同一行声明多个常量或变量,以逗号分隔:
var x = 0.0, y = 0.0, z = 0.0


提示:如果代码中需要存储的值不会改变,务必通过let 关键字作为常量声明。只有存储需要改变的值时才需要使用变量。
2. 类型说明
声明常量或变量时可提供类型说明,明确指定该常量或变量所能存储的数据类型。类型说明的写法为,在常量或变量名称后加上一个冒号、一个空格,后接要使用的类型名称。
本例为名为 welcomeMessage 的变量提供类型说明,指明该变量可存储 String 型的值:
var welcomeMessage: String

声明语句中的冒号意为“…的类型为…”,因此上面的代码可以这样理解:
“声明一个名为 welcomeMessage 的变量,其类型为 String。”其中“类型为 String”代表“可存储任意 String 类型的值”。可以理解为可以存放的“东西的类型”(或“东西的种类”)。
welcomeMessage 变量现在可以赋任意字符串值,不会报错:
welcomeMessage = "Hello"

提示:在实践中需要编写类型说明的情况非常罕见。如果你在定义常量或变量时提供了初始值,Swift 通常能够推断出该常量或变量应使用的类型,详情参见类型安全及类型推断。在上述 welcomeMessage 例子中,没有提供初始值,因此才通过类型说明给 welcomeMessage 变量明确指定类型,而没有让它通过初始值推断。
3. 常量与变量的命名
常量及变量的名称可以使用几乎所有字符,包括 Unicode 字符:
let π = 3.14159
let 你好 = "你好世界"
let 🐶🐮 = "汪哞"

常量及变量的名称不可以包含数学符号、箭头、私有(即无效的)Unicode 码位 或绘制线条/方框用的字符。并且名称不能以数字开头,但除了开头的其他地方都可以使用数字。
常量及变量一旦声明为一个特定类型,则不能以同样的名称再次声明,也不能更改使其存放不同类型的值。同样,也不允许将常量再次声明为变量,或将变量再次声明为常量。
提示:如果需要将常量或变量命名为 Swift 的保留字,可以在将该关键字作为名称使用时用反引号(`)包围。尽管如此,你还是应该避免将关键字作为名称使用,除非迫不得已。
可以将现有变量改赋兼容类型的值。在下例中,friendlyWelcome 被从 "Hello!" 改为 "Bonjour!"
var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome 现在为"Bonjour!"

与变量不同,常量的值一旦确定则不可更改。尝试更改则会报编译错误:
let languageName = "Swift"
languageName = "Swift++"
// 编译时错误 -languageName 不可更改

4. 输出常量及变量
可以通过 println 函数输出常量或变量的当前值:
println(friendlyWelcome)  // 输出 "Bonjour!"

println 是一个全局函数,可向适当的输出界面输出值,末尾接换行符。例如在Xcode 环境开发,println 会将输出内容输出至 Xcode 的“控制台”面板。(另一个函数 print 执行几乎一样的操作,不同之处在于,后者不会在待输出值的末尾添加换行符。)
println 函数可输出传给它的任何 String 值:
println("这是一个字符串")   // 输出 "这是一个字符串"

println 还能输出更复杂的日志消息,用法与 Cocoa NSLog 函数相似译注:即 C 语言的 printf() 风格。消息内容可以包括常量与变量的当前值。
Swift 通过字符串内插(string interpolation)将常量或变量的名称作为占位符内嵌到较长的字符串中,借此提示 Swift将其替换为常量或变量的当前值。将名称置于括号之间,并在左括号之前通过反斜杠转义:
println("friendlyWelcome 当前的值为 \(friendlyWelcome)")
// 输出"friendlyWelcome 当前的值为 Bonjour!"

注:字符串内插可用的所有选项参见 字符串内插 一节。
1.2 注释
通过注释,可在代码中嵌入不可执行的文本,作为笔记或对自己的提示。在编译代码时,Swift 编译器会忽略注释内容。
Swift 中的注释与 C 语言非常相似。单行注释以两个左斜杠开头(//):
// 这是一条注释

还可以编写多行注释,以左斜杠加星号(/*)开头,并以星号加左斜杠(*/)结尾:
/* 这也是一条注释,
但跨越多行 */

C 语言的多行注释有所不同的是,Swift的多行注释可以嵌套在其他多行注释内部。写法是在一个多行注释块内插入另一个多行注释。第二个注释块封闭时,后面仍然接着第一个注释块:
/* 这是第一个多行注释的开头
/* 这是嵌套的第二个多行注释 */
这是第一个多行注释的结尾 */

嵌套式多行注释允许你迅速简便地注释掉大段代码,而不用担心这段代码中是否已经存在多行注释。
1.3 分号
与其他很多语言不同,Swift 不要求在每条语句末尾加上分号(;),但只要你愿意,加上也无妨。不过,如果要在同一行书写多条语句,分号是必需的:
1.4 整数
整数(integer)指没有小数部分的整数,如 42 -23。整数既可以是有符号的(signed,正数、零、负数)也可以是无符号的(unsigned,正数或零)。
Swift 提供 8163264 位形式的有符号及无符号整数。这些整数类型遵循 C 语言的命名规约,如 8 位无符号整数的类型为 UInt832 位有符号整数的类型为 Int32。与 Swift 中的所有类型一样,这些整数类型的名称以大写字母开头。
5. 整数的边界
各整数类型允许的最小值及最大值可通过 min max 属性获得:
let minValue = UInt8.min   // minValue等于 0,类型为 UInt8
let maxValue = UInt8.max  //maxValue 等于 255,类型为 UInt8

这些属性的值的类型与对应宽度的数据类型一致(如上例为 UInt8),因此也可以在表达式中与同类型的其他数值一起使用。
6. Int
绝大多数情况下,你并不需要自己决定代码中要使用的整数宽度。Swift 还提供了一个整数类型 Int,其宽度与当前平台的原生字长(word size)一致:
32 位平台,Int Int32 宽度一致。
64 位平台,Int Int64 宽度一致。

除非你需要处理特定宽度的整数,在代码中应该只使用 Int 表示整数。这样可以保证一致性及互运算性。即使是在 32 位平台,Int 也能存储 -2,147,483,648 2,147,483,647 的任意数值,对于很多整数区间需求来说已经足够大了。译注:信苹果会丢饭碗的
7. UInt
Swift 还提供了无符号整数类型 UInt,其宽度与当前平台的原生字长一致:
32 位平台,UInt UInt32 宽度一致。
64 位平台,UInt UInt64 宽度一致。

注:只有在特别需要宽度与平台原生字长一致的时才需要使用无整数类型 UInt。否则应使用 Int,即使要存储的值一定非负。总使用 Int 表示整数值有助于保证代码互运算性、避免不同数据类型的转换,并且与整数类型推断相匹配,参见 类型安全及类型推断。
1.5 浮点数
浮点数表示有小数部分的数字,例如 3.141590.1 -273.15
浮点数类型可以表示的值比整数类型宽广得多,也能存储 Int 类型能存放的最大及最小值。Swift提供两种有符号的浮点数类型:
Double 表示一个64 位浮点数。在浮点数值非常大或非常精确时使用它。
Float 表示一个 32 位浮点数。在浮点数值不需要 64 位精度时使用它。

注意:Double 的精度为 15 个十进制有效数字,而 Float 的精度只有 6 位十进制有效数字。应根据代码所需数值的特点及值域选用合适的浮点数类型。
1.6 类型安全及类型推断
Swift 是一门类型安全的语言。类型安全要求代码中值的类型非常明确。如果代码中要求提供 String 数据,则不会错误地向它传递 Int 数据。
由于 Swift 类型安全,它会在编译代码时执行类型检查,并将任何类型不匹配的地方标为错误。这样可以在开发过程中尽可能早地发现并修复问题。
类型检查有助于在操作不同类型值时避免错误。然而,这并不意味着你必须为声明的每个常量与变量指定类型。如果你不指定所需值的类型,Swift 会通过类型推断(typeinference)求得适当的类型。类型推断允许编译器在编译代码时,根据你提供的值,自动推测出特定表达式的类型。
得益于类型推断,Swift 对类型声明的需要比起 C Objective-C 语言而言要少很多。常量与变量仍然有明确的类型,但明确指定类型的工作已经由编译器代你完成。
类型推断在你声明常量或变量的同时提供初始值时尤其有用。通常通过在声明时赋字面值(literal value,或称“字面量”literal)实现。(字面值指直接出现在源代码中的值,如下例中的 42 3.14159。)
例如,如果将字面值 42 赋给新的常量,而不明确讲它是什么类型,Swift 会推断出你希望该常量为Int 类型,因为你初始化时提供的数字像是个整数:
let meaningOfLife = 42
// meaningOfLife 被推断属于 Int 类型

类似地,如果不为浮点数字面量指定类型,Swift 会推断出你希望创建一个Double 变量:
let pi = 3.14159
// pi 被推断属于 Double 类型

Swift 在推断浮点数类型时总会选用 Double(而不用 Float)。
如果在表达式中同时使用整数与浮点数字面量,将根据上下文推断得到 Double 类型:
let anotherPi = 3 + 0.14159
// anotherPi 也被推断为 Double 类型

字面值 3 没有明确的类型,自身也不属于某个明确的类型,但由于加法中出现了浮点数字面量,因此推断出合适的输出类型为 Double
1.7 数字字面量(Literals
整数字面量可以以下面的形式书写:
l 十进制数,无需前缀
l 二进制数,以 0b 为前缀
l 八进制数,以 0o 为前缀
l 十六进制数,以 0x 为前缀
下述整数字面量的值均为十进制的 17
let decimalInteger = 17 = 17
let binaryInteger = 0b10001      // 17 的二进制表示
let octalInteger= 0o21          // 17 的八进制表示
let hexadecimalInteger = 0x11   // 17 的十六进制表示

浮点数字面值可以为十进制(无需前缀),也可以是十六进制(以 0x 为前缀)。小数点两侧均必须有数字(或十六进制数字)。还可以有一个可选的幂次(exponent),对十进制浮点数为大写或小写的 e,对十六进制浮点数为大写或小写的 p
对幂次为 exp 的十进制数,基数将乘以10exp
l 1.25e2means 1.25 × 102, or 125.0.
l 1.25e-2means 1.25 × 10-2, or 0.0125.
对幂次为 exp 的十六进制数,基数将乘以2exp
l 0xFp2means 15 × 22, or 60.0.
l 0xFp-2means 15 × 2-2, or 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

1.8 数字类型转换
Int 类型应作为所有常规用途的整数常量及变量的类型,即使它们确实非负。通常情况下,使用默认的整数类型意味着这些整型常量与变量均可即时互相参与运算,并可与根据整数字面值推断出的类型相匹配。
仅当手中的任务必须使用其他整数类型时才用它们,如外部数据源提供宽度明确的数据,或为了性能、内存占用等其他必需优化考虑。在这些情况下使用宽度明确的类型有助于发现偶然的数值溢出,并还原这些数据实际使用时的特点。
8. 整数转换
不同类型的整数常量或变量所能存储的值域不同。Int8 常量或变量能存储 -128 127,而 UInt8 常量或变量能存储 0 255。无法存放进某常量或变量的数字会报编译时错误:
let cannotBeNegative: UInt8 = -1
// UInt8 不能存储负数,因此会报错
let tooBig: Int8 = Int8.max + 1
// Int8 不能存储大于其最大值的数字,
// 因此这里也会报错

由于不同数据类型能存储的值域不同,在进行数据转换时需要具体问题具体对待。这种实际选择的过程可避免隐式转换的问题,并使类型转换的意图在代码中明确地展现出来。
要将一种数据类型转换为另一种,应使用现有值初始化一个所需类型的新数。下例中,常量 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 值的和。

某类型(赋初始值) 是调用 Swift 类型构造函数并传递初始值的默认方法。幕后运作情况是,UInt16 有一个接受 UInt8 值的构造函数,因此该构造函数会被用于根据现有 UInt8 创建新的 UInt16。不过,在这里并不能传入任意类型——只能传入 UInt16 提供有构造函数的类型。扩展现有类型使其提供接受新类型(包括自己定义的类型)的构造函数的方法请见 扩展 一章。
9. 整数与浮点数转换
整数与浮点数类型之间的转换必须显式指定:
let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi 等于 3.14159,推断得到的类型为 Double

这段代码中,常量 three 被用来创建新的 Double 类型值,这样加法两侧的类型才一致。不进行类型转换的话,两侧将不允许相加。
浮点数到整数的逆向转换同样可行,整数类型可以用 Double Float 值初始化:
let integerPi = Int(pi)
// integerPi 等于 3,推断得到的类型为 Int

这样用浮点数初始化新整数时,浮点数值总会被截断。即, 4.75 变为 4 -3.9 变为 -3
多个数字常量或变量的结合规则与数字字面量的结合规则不同。字面量 3 可以直接与字面值0.14159 相加,因为数字字面量没有明确指定类型,它们自身也没有明确的类型。其类型仅当被编译器求值时才推断得出。
1.9 类型别名(aliases
类型别名(type aliases)为现有类型定义可替代的名称。类型别名通过 typealias 关键字定义。类型别名在需要以上下文中更为合适的名称称呼某个现有类型时非常有用,例如当处理来自外部数据源的特定宽度的数据时:
typealias AudioSample = UInt16

类型别名定义完成后,即可在可能用到原名的地方使用别名:
var maxAmplitudeFound = AudioSample.min
// 已发现的最大振幅 现在为 0

此处音频采样 作为 UInt16 的别名定义。因为它是别名,因此对音频采样.min 的调用实际上会调用 UInt16.min,最终为 已发现的最大振幅 变量提供初始值 0
1.10 布尔值
Swift 实现了基本的布尔(boolean)类型,称为 Bool。布尔值也称为逻辑值(logical),因为只能为真(true)或假(false)。Swift 提供了两种布尔常量值:true false
let orangesAreOrange = true
let turnipsAreDelicious = false

orangesAreOrange turnipsAreDelicious很好吃 的类型被推断为 Bool,因为它们以布尔字面值初始化。与上文中的 Int Double 一样,并不需要明确声明为 Bool,只要在创建变量的同时用 true false 初始化。以已知类型的值初始化常量或变量时,类型推断使 Swift 的代码更简练、更具可读性。
控制条件语句(如 if 语句)时,布尔值尤其有用:
if turnipsAreDelicious {
    println("Mmm, tastyturnips!")
} else {
    println("Eww, turnipsare horrible.")
}
// prints "Eww, turnips are horrible."

if 等条件语句的详细情况请见“流程控制”
Swift 的类型安全特性可避免非布尔值被当作 Bool 使用。下面的例子会报编译时错误:

let i = 1
if i {
    // 本例无法通过编译,报编译错误
}

换成下例便可以通过:
let i = 1
if i == 1 {
    // 本例可成功编译
}

i == 1 比较的结果类型为 Bool,因此第二个例子可以通过类型检查。i == 1 这类的比较在“基本运算符”一章讨论。
Swift 中的其他类型检查规则一样,这些规则可避免偶然错误,并确保各段代码的目的总是明确的。
1.11 元组
元组将多个值组合为单个值。元组内的值可以是任意类型,各元素不必是相同的类型。
在本例中,(404, "Not Found") 是描述一条 HTTP 状态码(HTTPstatus code)的元组。HTTP 状态码 是请求任何网页时,web 服务器返回的特殊值。如果请求了不存在的网页,则会返回状态码 404 Not Found
let http404Error = (404, "Not Found")
// http404Error is of type (Int, String), and equals (404, "NotFound")

(404,"Not Found") 元组将一个 Int 值与一个 String 值组合起来,表示 HTTP 状态码的两个值:一个数字和一个供人类阅读的描述。它可以这样描述:“类型为(Int, String) 的元组”。
你可以将类型任意排列来创建元组,它们可以包含任意多种不同的类型。只要你愿意,创建类型为 (Int, Int, Int) (String, Bool) 的元组也不会有问题,只要这种排列对你有意义。
元组的内容可以还原(decompose)为独立的常量或变量,然后便可照常访问:

let (statusCode, statusMessage) = http404Error
println("The status code is \(statusCode)")
// prints "The status code is 404"
println("The status message is \(statusMessage)")
// prints "The status message is Not Found"

如果你只需要元组的一部分值,可以在还原元组时用下划线(_)忽略掉其他部分:
let (justTheStatusCode, _) = http404Error
println("The status code is \(justTheStatusCode)")
// prints "The status code is 404"

还可以通过以 0 开头的索引号访问元组的各个元素值:
println("The status code is \(http404Error.0)")
// prints "The status code is 404"
println("The status message is \(http404Error.1)")
// prints "The status message is Not Found"

还可以在定义元组时为各元素命名:
let http200Status = (statusCode: 200, description: "OK")

为元组各元素命名后,便可以通过元素名称访问各元素的值了:
println("The status code is \(http200Status.statusCode)")
// prints "The status code is 200"
println("The status message is\(http200Status.description)")
// prints "The status message is OK"

元组在作为函数返回值时尤其有用。一个获取网页内容的函数可能会返回 (Int, String) 元组类型,来描述网页装取是成功还是失败。函数返回两个类型完全不同的值描述结果,所能提供的信息比只能返回固定类型的单个值要有用得多。详情请参见返回多个返回值的函数。
元组对临时组合相关的多个值非常有用。它们并不适合用来创建复杂的数据结构。如果你的数据结构的生命期超过临时使用的范畴,请将它作为类或结构建模,而不是以元组存储。详情请见类与结构。
1.12 可选量
在值可能不存在时使用可选量(optional)。可选量是指:
l 存在一个值,这个值等于 x
或者
l 不存在任何值
注:可选量的概念在 C Objective-C 中并不存在。Objective-C 中最相近的是,一个以对象为返回值的方法,可以返回 nil,表示“不存在有效的对象”。不过,该规则只对对象有效——对于结构体、基本的 C 类型或枚举值均不起作用。对于这些类型,Objective-C 语言的方法通常会返回一个特殊值(如 NSNotFound)来表示值不存在。这种策略假定该方法的调用方知道要测试返回值是否等于某个特殊值,并且记得要作此检查。Swift 的可选量允许表示任何类型不存在值,无需定义特殊常量。
举例说明。Swift String 类型有一个名为 toInt 的方法,可尝试将 String 值转为 Int 值。然而,不是所有字符串都可以转换为整数。字符串"123" 可以转换为数值 123,而字符串"hello, world" 却显然没有对应的数值。
下面的例子会利用 toInt 方法,尝试将 String 转换为 Int
let possibleNumber = "123"
let convertedNumber = possibleNumber.toInt()
// 转换得到的数字 被推断为 "Int?" 类型,即"可选的 Int"

由于 toInt 方法可能转换失败,因此它会返回一个可选的 Int 型,而不是 Int 型。可选的 Int 记作 Int?,而不是 Int。其中的问号表示该类型包含的值是可选的,即 Int? 可能包含某个 Int 类型的值,也可能不含任何值。(但不能包含其他类型的值,如 Bool 值或 String 值。不是 Int 就是不存在。)
10. if 语句与强制拆包
可以使用 if 语句测试可选量是否包含值。如果存在,则求值结果为 true;否则为 false
一旦确认可选量的确包含值,便可以通过在变量名末尾添加感叹号(!)访问其内部的值。感叹号明确表达:“我知道这个可选量的确存在值;请使用那个值。”这种操作称为对可选值进行强制拆包(force-unwrap):
if convertedNumber {
    println("\(possibleNumber)has an integer value of \(convertedNumber!)")
} else {
   println("\(possibleNumber) could not be converted to aninteger")
}
//输出 "123 的整数值为 123"

注:尝试用 ! 访问不存在的可选值时会导致运行时错误。在用 ! 强制拆包之前,务必确保可选量的确包含非 nil 的值。
11. 可选值绑定
可以通过可选值绑定(optional binding)测试可选量是否包含一个值,如果存在,则将该值以临时常量或变量的形式拆包使用。可选值绑定可以与 if while 语句结合使用,这样只需要一步就可以检查是否存在值、提取该值、并存放到常量或变量中。关于 if while 语句的更多情况在 流程控制 一章中讲解。
if 语句为例,可选值绑定可以这样书写:
if let constantName = someOptional {
    statements
}

上文中可能是数字 一例,可以改写用可选值绑定代替强制拆包:
if let actualNumber = possibleNumber.toInt() {
   println("\(possibleNumber) has an integer value of\(actualNumber)")
} else {
   println("\(possibleNumber) could not be converted to aninteger")
}
// 输出 "123 的整数值为 123"

可以这样理解:

l “如果 可能是数字.toInt 返回的 可选的 Int 包含一个值,则新建一个名为 实际值 的常量,并将其值设为可选量中包含的值。”
l 如果转换成功,常量 实际值 将可供 if 语句的第一段分支使用。该常量已经以可选量内部的值初始化,因此不再需要用后缀 ! 访问其值。本例中,实际值 被直接用来输出转换结果。
常量与变量均可用于可选值绑定。如果需要在第一个分支中修改实际值 的值,可以改写为 if var 实际值,这样可选量的值将作为变量而非常量拆包。
12. nil
要将可选变量设为值不存在的状态,可以给它赋特殊值 nil
var serverResponseCode: Int? = 404
//服务器响应码 包含一个实际存在的 Int 值:404
serverResponseCode = nil
//服务器响应码 现在不含任何值

注:nil 不能用于非可选量。如果代码中的常量或变量需要适配值不存在的特殊情况,务必将它声明为恰当的可选类型。
如果定义的可选量时不提供默认值,该常量或变量将自动设为 nil
var surveyAnswer: String?
// surveyAnswer 被自动设为 nil

注:Swift nil Objective-C nil 不同。Objective-C nil 是指向不存在对象的指针。而Swift nil 不是指针——它代表特定类型的值不存在。任何类型的可选量都能赋值为 nil,而不仅限于对象类型。
13. 可选量的隐式拆包
如上所述,可选量指允许“值不存在”的常量或变量。可选量可以通过 if 语句测试是否存在值,也可以通过可选值绑定按条件拆包,并在值存在的情况下才访问可选量的值。

有时根据程序结构可以推断,可选量在首次赋值后,必然存在值。这些情况下,可以不必每次访问时都检测并提取可选量的值,因为可以安全地认为那时一定存在值。
这些可选量可定义为隐式拆包的可选量(implicitly unwrapped optional)。隐式拆包的可选量的声明格式为,在希望标为可选的类型名称后面,用感叹号 (String!) 代替问号 (String?)
隐式拆包的可选量在可选量首次定义后即确认存在值,在此之后任何时刻都肯定存在的时候有用。Swift 中主要应用在类初始化,详见 外部引用与隐式拆包的可选属性。
隐式拆包的可选量在实现级别就是普通的可选量,但能够像非可选量那样使用,无需在每次访问时显式拆包。下例显示了可选的 String 隐式拆包的可选 String 之间的行为差异:
let possibleString: String? = "An optional string."
println(possibleString!) //  // 访问其值时需要添加感叹号
//输出 "可选的 String

let assumedString: String! = "An implicitly unwrapped optionalstring."
println(assumedString)  //访问其值时无需感叹号
//输出 "隐式拆包的可选 String"

可以认为,隐式拆包的可选量即授予可选量在被使用时自动拆包的权限。不必每次使用可选量时都在名称后面添加感叹号,只需在定义时在类型后面加上感叹号即可。
注:如果在隐式拆包的可选量存在值之前就尝试访问,会触发运行时错误。结果与在普通可选量尚未赋值时直接加感叹号引用相同。
隐式拆包的可选量也可以当作普通可选量对待,检查是否存在值:
if assumedString {
    println(assumedString)
}
// 输出 "隐式拆包的可选 String"

隐式拆包的可选量同样可以结合可选值绑定使用,单条语句完成检查值并拆包的工作:
if let definiteString = assumedString {
    println(definiteString)
}
// 输出 "隐式拆包的可选 String"

注:当变量在后续过程中可能变为 nil 时,不应使用隐式拆包的可选量。如果在变量声明周期内都需要检查 nil 值,请务必使用普通的可选类型量。
1.13 断言(Assertions
可选量允许检查值的存在与否,并允许代码能够适配不存在值的情况。但也有时候,如果值不存在,或不满足特定条件,代码便不可能继续执行下去。对于这些情况,需要在代码中触发断言(assertion)译注:“触发”指断言不成立来终止执行,并为找出值不存在或无效的原因创造机会。
14. 借助断言辅助调试
断言是一种运行时检查,确认一定为 true 的逻辑条件是否成立。即,断言“宣告”某条件一定成立。使用断言,可确保在进一步执行后续代码之前,确保必要条件确实已满足。如果条件的求值结果为 true,代码将照常继续运行;如果条件的求值结果为 false,则代码不再继续执行,应用程序随之终止。
如果是在调试环境运行时触发断言(如在 Xcode 中构建并执行应用程序),你将确切地知道异常情况出现的位置,并能在程序执行到该断言位置的状态下调试程序。断言还允许提供一段合适的调试消息,对该断言加以说明。
断言可通过调用全局函数 assert 来实现。将需要求值的表达式(结果为 true false)传递给 assert 函数,如果条件求值结果为 false,则应显示错误消息:
let age = -3
assert(age >= 0, "A person's age cannot be less thanzero")
//断言触发错误,因为 age>= 0 不成立

本例中,代码仅在 age >= 0 的求值结果为 true 时才继续执行,即,age 的值为非负数才继续。如果 age 的值为负数,即上述代码中的情况,则 age >= 0 的求值结果为 false,于是断言触发,应用程序终止。
断言的消息内容不能使用字符串内插。如果需要,可省略断言消息,如下例所示:

assert(age >= 0)

15. 时应使用断言
仅当条件可能为假、但必须一定为真代码才能继续执行时才使用断言。适合运用断言检查的场景包括:
l 向自定义下标实现传递了整数下标索引,但该索引号可能太小或太大。
l 值传递给了函数,但如果值无效,函数无法完成其任务。
l 可选值当前为 nil,但后续代码要求值非 nil方可成功执行。
参见下标 函数。
注:断言可使应用程序终止。断言不适合设计不太可能出现无效条件的场景。尽管如此,在可能出现无效条件的情况下,断言仍不失为在开发过程中确保这些问题在发布以前得到关注与处理的有效途径。
2基本运算符

运算符(operator)是用于检查、更改或组合值的特殊符号或短语。例如,加法运算符(+)求两个数字的加和(用例let i = 1 + 2)。更复杂的例子包括逻辑与(logicalAND)运算符&&(用例 if 已输入门禁密码 && 已通过视网膜扫描) 以及自增运算符 ++i,后者是将 i 存储的值加上 1 的便捷写法。
Swift 支持标准 C 的大多数运算符,并改进了部分行为特性,以避免常见的编码错误。赋值运算符(=)不会返回值,这样可避免在打算使用等于运算符(==)的地方误用前者。算术运算符(+-*/% 等)会侦测并阻止值溢出,可避免处理超出可存储值域范围的数时出现意料之外的结果。如果需要支持溢出,可以选用 Swift 的溢出运算符,详见 溢出运算符。
C 语言不同,Swift 允许对浮点数求余(%)。Swift 还提供了两种 C 语言没有的范围运算符(a..b a...b),作为表达值域范围的便捷写法。
本章讲解 Swift 中的常用运算符。高级运算符一章涉及了 Swift 的高级运算符,并讲解了如何自定义运算符,以及让标准运算符支持自定义类型的运算。
2.1 术语
运算符分为一元、二元以及三元运算符:
l 一元运算符(unary operator)对单个目标进行运算(如 -a)。一元前缀运算符(unaryprefix operator)置于运算目标之前(如 !b),而一元后缀运算符(unary postfix operator)紧跟目标之后(如 i++)。
l 二元运算符(binary operator)对两个目标进行运算(如 2 + 3),它们属于中缀(infix)运算符,因为出现在运算对应的两个目标之间。
l 三元运算符(ternary operator)对三个目标进行运算。与 C 语言一样,Swift 只有一个三元运算符:三元条件运算符(即 a ? b : c)。
运算符操作的值称为运算数(operands)。在表达式 1 + 2 中,+ 符号是二元运算符,它的两个运算数为值 1与值 2
2.2 赋值运算符
赋值运算符(assignment operatora = b)用 b 的值初始化或更新 a 的值:
let b = 10
var a = 5
a = b
// a 的值现在等于 10

如果赋值语句的右侧是包含多个值的元组,其元素可一次性提取为多个常量或变量:
let (x, y) = (1, 2)
// x 等于 1y 等于 2

C / Objective-C 语言的赋值运算符不同,Swift 语言的运算符本身不会返回值。因此下面的语句不正确:
if x = y {
// 语句无效,因为“x = y”不会返回值
}

该特性可避免在希望使用等于运算符(==)的地方误用赋值运算符(=)。通过不承认 if x = y 的有效性,Swift 将帮助你避免代码中出现这样的错误。
2.3 算术运算符
Swift 支持对所有数字类型使用四种标准的算术运算符(arithmeticoperator):
l 加法(+
l 减法(-
l 乘法(*
l 除法(/
1 + 2 // 等于 3
5 - 3 // 等于 2
2 * 3 // 等于 6
10.0 / 2.5 // 等于4.0

C / Objective-C 语言的算术运算符不同,Swift 的算术运算符默认不允许值溢出。若要支持溢出特性,可以使用 Swift 的溢出运算符(如 a &+ b)。参见 溢出运算符。
加法运算符还支持 String 连接:
"hello, " + "world" // 等于 "hello, world"

可以将两个 Character 值相加,或将一个 Character 值与一个 String 值相加,得到新的 String 值:
let dog: Character = "🐶"
let cow: Character = "🐮"
let dogCow = dog + cow
// dogCow is equal to "🐶🐮"

参见字符串及字符的连接。
2.3.1 求余运算符
求余运算符(remainder operatora % b)求出) a 能装得下多少个整数的 b,并返回剩余的空间大小(即整除后的余数 remainder)。
注:求余运算符(%)在其他语言中也称作求模运算符(modulo operator)。但对负数的运算结果显示,Swift 语言的实现是严格的求余操作,而非求模操作。

求余运算符的原理如下。要计算 9 % 4,首先要求出 9 里面能装得下多少个 4
                              
如图显示,9 里面能装得下两个 4,因此余数为 1(橙色部分)。

Swift 语言的写法为:
9 % 4 // 等于 1

要求出 a % b 的结果,% 运算符会计算下面的式子,并将 余数 作为输出结果返回:
a = (b × 某个乘数) + 余数

其中某个乘数 a 中能装下 b 的最大数目。
9 4 代入等式,可求得:
9 = (4 × 2) + 1

a 为负值时,求余数的方法不变:
-9 % 4 // 等于-1

-9 4 代入等式,可求得:
-9 = (4 × -2) + -1

得到的余数值为 -1
b 为负值时,b 的符号将被忽略。因此 a % b a % -b 将总得到相同的结果。
2.3.2 浮点数的余数计算
C / Objective-C 的余数运算符不同,Swift 的余数运算符还能对浮点数进行计算:
8 % 2.5 // 等于0.5

上例中,8 除以 2.5 等于 3,余数为 0.5,因此余数运算符返回Double 0.5

2.3.3 自增与自减运算符
C 语言类似,Swift 也提供了自增运算符(increment operator++)与自减运算符(decrement operator--),作为将数字变量的值加上或减去 1 的便捷写法。这两个运算符可以对任何整型或浮点型的变量使用。
var i = 0
++i // i 现在等于1

每当调用 ++i 时,i 的值就会增加 1。通常来说,++i 就是 i = i + 1 的便捷写法。类似地,--i 也相当于 i = i - 1
++ -- 两对符号可以作为前缀或后缀运算符使用。++i i++ 均可用来将 i 的值加 1。类似地,--i i-- 均可用来将 i 的值减去 1
注意这些运算符不仅会改变 i 的值,还会返回一个值。如果你只需要自增或自减后的值存放在 i 中,可以忽略运算符的返回值。但如果你确实要用到返回值,要注意前缀及后缀运算符返回值的差异,规则如下:
l 如果运算符写在变量的前面,则先改变变量值,再返回其值。
l 如果运算符写在变量的后面,则先返回原值,后改变变量值。
例如:
var a = 0
let b = ++a
// a b 现在均为 1
let c = a++
// a 现在为 2,但 c 被设为自增前的值 1

在上例中,let b = ++a 先增加 a 的值,然后才返回它的值。因此a b 都等于新的值 1
但是,let c = a++ 先返回 a 的原值,然后才增加 a 的值。即 c 得到了原值 1,然后 a 被更新为新值 2

除非你需要利用 i++ 的这一特性,建议你在所有情况下都使用 ++i --i,因为它们的动作符合传统思维的期望:先修改 i 的值,然后返回改变后的结果。
2.3.4 一元减号运算符
可以在数值前面加上 - 来切换符号。这个前缀运算符- 就称为一元减号运算符(unaryminus operator):
let three = 3
let minusThree = -three       // minusThree equals -3
let plusThree = -minusThree  // plusThree equals 3, or "minus minus three"

一元减号运算符(-)直接加在所要操作的值之前,无需任何空格。
2.3.5 一元加号运算符(unary plus operator+
直接返回所操作的值,不作任何处理:
let minusSix = -6
let alsoMinusSix = +minusSix // alsoMinusSix equals -6

尽管一元加号运算符实际上不作任何运算,代码中仍然可以用它提供一些语义信息,与表示负数的一元减号运算符形成对比。
2.4 组合赋值运算符
C 语言类似,Swift 也提供组合赋值运算符(compound assignmentoperator),可将赋值运算符(=)与其他运算组合起来使用。例如加法赋值运算符(additionassignment operator+=):
var a = 1
a += 2
// a 现在等于 3

表达式 a += 2 a = a + 2 的便捷写法。加法与赋值操作被组合到一起,单个运算符就可以完成两项操作,非常高效。
注:组合赋值运算符不返回值。因此不能写成如 let b = a += 2。该行为与前文提到的自增/自减运算符不同。
组合运算符的完整列表可在语言参考:表达式 一节找到。
2.5 比较运算符
Swift 支持标准 C 的全部比较运算符(comparison operator):
l 等于(a == b
l 不等于(a != b
l 大于(a > b
l 小于(a < b
l 大于或等于(a >= b
l 小于或等于(a <= b
注:Swift 还提供了两个鉴别运算符(identityoperator=== !==),可用来测试两个对象引用是否指向同一个对象实例。详情请见类与结构 一节。


每个比较运算符都会返回一个 Bool 值,告知语句是否成立:
1 == 1 // true,因为1 等于 1
2 != 1 // true,因为2 不等于 1
2 > 1 // true,因为2 大于 1
1 < 2 // true,因为1 小于 2
1 >= 1 // true,因为1 大于或等于 1
2 <= 1 // false,因为2 既不小于又不等于 1

通常在条件语句中使用比较运算符,如在 if 语句中:
let name = "world"
if name == "world" {
println("hello, world")
} else {
println("不好意思啊,\(name),俺不认得你")
}
// 输出 "hello,world",因为 name 的确等于 "world"

关于 if 语句的更多情况,请见 流程控制。
2.6 三元条件运算符
三元条件运算符(ternary conditional operator)是一种特殊的运算符,由三部分组成,形式为问题 ? 回答1 : 回答2。它根据 问题 成立与否,从两个表达式中取出一个并求值。如果 问题成立,则求 回答1 的结果并返回其值;否则求出 回答2 的结果并返回其值。

三元条件运算符是下述代码的便捷写法:
if question {
    answer1
} else {
    answer2
}

下面的例子将计算表格某行的显示高度。如果该行有表头,则行高应比内容高度高 50 个像素;如果没有表头,则只高出 20 个像素:
let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// rowHeight is equal to 90

上例是下列代码的便捷写法:
let contentHeight = 40
let hasHeader = true
var rowHeight = contentHeight
if hasHeader {
    rowHeight = rowHeight + 50
} else {
    rowHeight = rowHeight + 20
}
// rowHeight is equal to 90

使用三元条件运算符的例子说明,可以仅用一行代码就将行高 设为正确的值。这比不用三元运算符的第二个例子简明得多,并且行高 无需定义为变量,因为不再需要用 if 语句修改其值。
三元条件运算符提供了二选一的高效写法。但使用三元条件运算符应小心。如果过度使用,其简明性可能导致代码难以阅读。应避免将多个三元条件运算符组合在同一个语句中。
2.7 区间运算符
Swift 包含两个区间运算符(range operator),是表达值域范围的便捷写法。
2.7.1 闭区间运算符
闭区间运算符(closed range operatora...b)定义了从 a b 的区间范围,包括 a b 两个值。
闭区间运算符在需要对某区间内所有值进行迭代时有用,如在 for-in 循环中使用:
for i in 1...5 {
println("\(i) 乘以 5 \(i * 5)")
}
// 1 乘以 5 5
// 2 乘以 5 10
// 3 乘以 5 15
// 4 乘以 5 20
// 5 乘以 5 25

关于 for-in 循环的更多信息,请见 流程控制。
2.7.2 半闭区间运算符
半闭区间运算符(half-closed range operatora..b)定义了从 a b 的区间,但不含 b。说是半闭,是因为第一个值包含在区间以内,但最后一个值不在区间内。
半闭区间在处理从 0 开始计数的列表时有用,如遍历数组,可从 0 数到列表的长度(但不包括长度值本身):
let names = ["Anna", "Alex", "Brian","Jack"]
let count = names.count
for i in 0..count {
    println("Person \(i +1) is called \(names)")
}
// Person 1 is called Anna
// Person 2 is called Alex
// Person 3 is called Brian
// Person 4 is called Jack

注意数组包含四个对象,但因为是半闭区间,所以 0..人数 只数到 3(数组中最后一个元素的索引号)。数组的详细情况请见 数组。
2.8 逻辑运算符
逻辑运算符组合布尔逻辑值 true false 或修改。Swift 支持基于 C 语言中的三个标准的逻辑运算符:
l 逻辑非 (!)
l 逻辑和 (&& b)
l 逻辑或 (| | b)。
l 逻辑 NOT 运算符
逻辑非运算符(logical NOT operator!a)将布尔值取反,即 true变为 false,而 false 变为 true
逻辑非运算符属于前缀运算符,置于所操作的值之前,不加任何空格。可以理解为“非 a”(not a),如下例所示:
let allowedEntry = false
if !allowedEntry {
    println("ACCESSDENIED")
}
// prints "ACCESS DENIED

代码中的 if !允许进入 可以解释成“如果不允许进入(if not 允许进入)”。下面一行代码仅当“不允许进入”成立时才会执行;即允许进入 false 时才执行。
在此示例中,布尔值常量及变量的名称应谨慎选择,方可确保代码简明又具可读性,同时避免出现双重否定或引起混淆的逻辑语句。。
2.8.1 逻辑与运算符
逻辑与运算符(logical AND operatora && b)用于构造这样的逻辑表达式:运算符两侧的值均为 true,整个表达式的求值结果才为true
如果有一个值为 false,整个表达式便为 false。事实上,如果第一个值为 false,第二个值将被直接忽略,而不会进一步对它求值,因为不论它为何值,整个表达式的值都不可能等于 true。这称为短路求值(short-circuitevaluation)。
下例考察两个 Bool 值,仅当两值均为 true 时才允许访问,则:
let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
   println("Welcome!")
} else {
    println("ACCESSDENIED")
}
// prints "ACCESS DENIED"

2.8.2 逻辑或运算符
逻辑或运算符(logical OR operatora || b)属于中缀运算符,由两个连续的竖线构成。它用来构造这样的表达式:整个表达式为 true 的条件为,两个值中至少有一个为 true
与前文的逻辑与运算符一样,逻辑或运算符在考察它的两个表达式时,也采用“短路求值”算法。只要逻辑或表达式左侧为 true,其右侧将不再求值,因为这时右侧的值对整个表达式的总体结果不再有影响。
下例中,第一个 Bool 值(有钥匙)为 false,但第二个值(知道备用密码)为 true。因为有一个值为 true,所以整个表达式的求值结果也为 true,因此允许访问::
let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
   println("Welcome!")
} else {
    println("ACCESSDENIED")
}
// prints "Welcome!"

2.8.3 组合逻辑运算符
您可以组合多个逻辑运算符来创建更长的复合表达式:
如果 enteredDoorCode && passedRetinaScan | |hasDoorKey ||knowsOverridePassword {
if enteredDoorCode && passedRetinaScan || hasDoorKey ||knowsOverridePassword {
   println("Welcome!")
} else {
    println("ACCESSDENIED")
}
// prints "Welcome!"

该例用到了多个 && || 运算符,构造了一条更长的组合表达式。不过,&& || 运算符操作的仍然是两个值,因此该组合表达式实际上是由三个较短的表达式连立而成的。它可以这样理解:
如果我们输入了正确的门禁密码、并且通过了视网膜扫描;或者如果我们有门钥匙;或者如果我们知道紧急的备用密码,则允许访问。
根据已输入门禁密码、已通过视网膜扫描、有钥匙 三个常量,前两个小表达式的值均为 false。不过我们知道紧急的备用密码,因此整个组合表达式的求值结果仍然为 true。。
2.8.4 显式圆括号
有时虽然从语法上看,括号并不是必需的,加上括号却很有用,它可以让复杂表达式的意图更显而易见。在上面门禁的例子中,为组合表达式的第一部分加上括号,可使其意图更明显:
if (enteredDoorCode && passedRetinaScan) || hasDoorKey ||knowsOverridePassword {
   println("Welcome!")
} else {
    println("ACCESSDENIED")
}
// prints "Welcome!"!"

括号将前两个值与其他值分隔开来,使其构成整体逻辑中一种可能状态的意图更为明显。组合表达式的结果不会改变,但对读者而言,整体意图更加清晰。可读性总是优先于简洁度;应尽可能在合适的地方使用括号,使你的意图更加明晰。


3字串和字符

     String 是一个有序的字符集合,例如 "hello, world", "albatross"。Swift 字符串通过 String 类型来表示,也可以通过Character 类型值的集合来表示。
Swift通过String 和 Character ,提供了一种快速、简洁,同时兼容Unicode方式,来处理代码中的文本信息。
     在Swift中创建和操作字符串的语法,跟C语言类似,简洁且易懂。连接字符串,可用“+”符号进行连接即可。与 Swift 中其他的值一样,能否更改字符串的值,取决于其被定义字符串的类型是常量还是变量。
     String的语法不但简易,而且提供了便捷快速的访问和操作方式,同时其都是有独立编码的Unicode 字符组成的。除了上面介绍的功能,String还可支持在其中插入常量、变量、字面量和表达式,以此对于实现自定义字符串的展示、存储和输出,显得轻松自如。

4集合类型

5控制流

6函数

7闭包(封装)

8枚举

9类和结构

在很多开发语言中,例如C++Pascal中,类和结构是通用的。由于结构的灵活性,在很多开发语言中,特别是非全面向对象开发语言中,其往往是程序构建代码的基石。在对于类和结构,俗语讲,它们更像没有进行装修的框架大厦,在装修的时候,采用的相同的装修方法,采用相同的常量、变量和函数等基元素,构建定义的属性和方法,然后嵌套到他们里面,浑然而成一座雄伟的大厦。
在具有以上的功能的同时,Swift更有自己的独有特性,即自定义类和结构,不需要像其他语言一样,需要创建头和源(实现)两个文件,而是在一个文件中可以独自自定义类或者结构,并且它们可以自动性的,由其他代码来引来使用。

在传统上,实例通常被称为对象。但是,在Swift中,实例也可以用于结构。在由于这个特点,在本章介绍实例,可以泛指为类的实例或者结构的实例。

9.1 类和结构的差异性
Swift中,类和结构有很多共同点:
l 定义属性,用来读取和存储值。
l 定义方法,以提供功能。
l 定义下标,用标语法来提供访问它们的值。
l 定义初始化,用于设置其初始状态。
l 采用扩展,以便于超越其默认实现的功能。
l 采用协议,以便于用来提供某种标准功能。
欲了解更多信息,请参见属性、方法、下标、初始化、扩展和协议。
相比而言,类还具有结构不具有的额外功能
l 能通过继承来继承另一个类的特性。
l 能通过类型转换在运行时来检查和解释类实例的类型。
l 能通过清空器来使类实例释放掉其被赋值的任何资源。
l 能通过引用计数,对一个类实例,实现多个引用。
欲了解更多信息,请参见继承、类型转换、初始化,并自动引用计数。

结构总是用来被复制,在代码周围传递时,并不使用引用计数。

9.2 语法
类和结构有相似的定义语法。定义类,采用class关键字,定义结构,采用struct关键字,然后把他们的整个定义一对大括号内:

class SomeClass {
    // class definition goeshere
}
struct SomeStructure {
    // structure definitiongoes here
}



在定义新的类或者结构时,按照Swift命名规则,都可高效地定义一个全新的Swift类型。只需给出与Swift类型(例如 String, Int,Bool)相匹配的大写字母命名的名称(例如这里的SomeClassSomeStructure) 。相反,与类和结构的命名不同,对于其属性和方法,要采用小写字母方式的名称(例如frameRateincrementCount)。

这里有一个结构定义和类定义的例子:
struct Resolution {
    var width = 0
    var height = 0
}
class VideoMode {
    var resolution =Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

上面的例子定义了一个新的结构称为Resolution,描述了一个基于像素的显示分辨率。该结构具有两个存储的属性称widthheight。存储的属性是常数或变量,打包存储为类或结构的一部分。这两个属性被推断为int类型设置到0的初始值。
上面的示例还定义了一个新的类称为VideoMode,描述视频显示一个特定的视频模式。这个类上有四个变量存储性能。第一, resolution,是一个新的进行初始化的 resolution结构实例,从而得到一个Resolution的属性类型。对于其他三个属性,新的VideoMode实例中,一个交错的初始化false的设置,一个值为0的播放帧率,一个可选的字符串值属性nameName属性是自动的无默认值,或“没有名字的值”,因为它是一个可选的类型。
9.2.1 类和结构实例
Resolution的结构定义和VideoMode的类定义,只描述一个ResolutionVideoMode的样子。他们自己并不描述一个特定的分辨率或视频模式。这样做,需要创建的结构或类的一个实例。
创建两​​种结构和类实例的语法很相似
let someResolution = Resolution()
let someVideoMode = VideoMode()

使用初始化器语法可用于创建结构和类的新实例。最简单的初始化语法形式,利用类或结构的名字遵循空括号的规则,来进行初始化处理,如resolution()videomode()。用这些方法可用来创建的类或结构的新实例,其初始化过程中,对应的任何属性都是采用的默认值。类和结构初始化在“初始化”章节将更详细地描述。

9.2.2 访问属性
使用点语法可以访问实例的属性。在点语法上,通过在实例名后,用句点分隔(.)来隔离,输入点分隔后将可立即写属性的名称,两者之间没有任何空格。
println("The width of someResolution is\(someResolution.width)")
// prints "The width of someResolution is 0"

在这个例子中,someResolution.width指是someResolutionwidth属性,并返回默认初始值0
可以深入到子属性,如在VideoModeresolutionwidth属性:
println("The width of someVideoMode is\(someVideoMode.resolution.width)")
// prints "The width of someVideoMode is 0"

还可以使用点语法将新值赋给一个变量属性:
someVideoMode.resolution.width = 1280
println("The width of someVideoMode is now\(someVideoMode.resolution.width)")
// prints "The width of someVideoMode is now 1280"



不像Objective-CSwift可以直接设置结构属性的子属性。上述最后一个例子中,someVideoModeresolution属性的width属性直接设置,而不需要对整个决议属性设置为一个新值。

9.2.3 对结构类型逐个成员化初始
每个结构都有一个自动生成的逐个成员初始化器,可以使用它来初始化新结构实例的成员属性。为新实例的属性的初始值,可以通过名称传递给执行逐个成员初始化:
let vga = Resolution(width: 640, height: 480)

与结构不同,类实例不接收默认以成员的初始化。初始值设定在“初始化”有更详细地描述。
9.3 结构和枚举是值类型
值类型,是复制类型,用来赋值给变量或常量,或者通过它来传递给函数。在前面的章节中,实际上,已经广泛使用了值类型。事实上,Swift中的所有基本类型,例如integersfloating-pointnumbersBooleansstringsarraysdictionaries,都是值类型,并作为幕后结构来实现。
所有的结构和枚举都是Swift值类型。这意味着,创建的任何结构和枚举实例,和任何作为属性的值类型,在代码中,进行传递时,都始终通过被复制来使用。
考虑一下这个例子,它使用的以前的例子的Resolution的结构:
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd

此示例声明一个名为hd的常数,并将其设置为一个已被初始化的全高清视频(1920×1080像素宽高)的宽度和高度的Resolution实例。
然后声明一个名为cinema的变量,并将其设置为HD的当前值。因为Resolution是一个结构,是由现有实例副本的制作而成,这新的副本被赋值到cinema。尽管HDcinema在具有相同的宽度和高度,但是在幕后他们是两个完全不同的实例。
接下来,cinema的宽度属性修改,修改成稍为2K的标准宽度,以用于数字电影放映(2048像素宽,1080像素高)。
cinema.width = 2048

检查cinemawidth属性,表明它确实已经更改为2048
println("cinema is now \(cinema.width) pixels wide")
// prints "cinema is now 2048 pixels wide"

然而,原始的HD实例的宽度属性仍然是1920年的旧值:
println("hd is still \(hd.width) pixels wide")
// prints "hd is still 1920 pixels wide"

cinema被赋予高清的当前值,存储在HD的值复制到新的cinema实例。最终的结果是两个完全不同的实例,正好包含相同的数值。因为他们是单独的实例,设置电影的宽度为2048,不影响存储在HD的宽度。
同样的行为适用于枚举:

enum CompassPoint {
    case North, South, East,West
}
var currentDirection = CompassPoint.West
let rememberedDirection = currentDirection
currentDirection = .East
if rememberedDirection == .West {
    println("Theremembered direction is still .West")
}
// prints "The remembered direction is still .West"

当对remembereddirection,赋予currentdirection值时,它实际上是设置为该值的副本。改变currentdirection值之后,不会影响存储在rememberedDirection原始值的副本。
9.4 类是引用类型
不同于值类型,引用类型,在被赋值到一个变量或常量时,或者当它们被传递给一个函数时,其不会被复制。而不是一个副本,相反,将会使用到同一个现有存在的实力的引用。
下面是一个例子,使用上面定义的VideoMode类:
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

这个例子声明了一个新的名为tenEighty常数,并将其设置为VideoMode类的新实例的引用。视频模式从以前的1080,赋值一个1920HD分辨率的副本。它被设置为隔行扫描,并给出“的1080i”的名称。最后,它被设置为每秒25.0帧的帧速率。
接着,tenEighty被赋值给一个新的名为alsoTenEighty的常数,alsoTenEighty的帧速率被修改:
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

因为类是引用类型,tenEightyalsoTenEighty实际上指的是同一个VideoMode实例。实际上,他们只是两个不同名称,而引用一个相同的单个实例。
检查tenEightyframeRate属性,表明其从底层的层VideoMode实例,正确地报告新的帧速率为30.0
println("The frameRate property of tenEighty is now\(tenEighty.frameRate)")
// prints "The frameRate property of tenEighty is now30.0"

需要注意的是tenEightyalsoTenEighty被声明为常量,而不是变量。但是,仍然可以改变tenEighty.frameRatealsoTenEighty.frameRate,因为tenEightyalsoTenEighty的值常数本身实际上没有改变。tenEightyalsoTenEighty本身不能存储 VideoMode实例,它们都是在幕后引用VideoMode实例。这样底层VideoModeframeRate属性被修改,而不是常量所引用VideoMode的值被修改。
9.4.1 特性运算符
因为类是引用类型,多个常量和变量幕后所指一个相同的单实例是可能的。如果两个常量或变量所指的是完全相同的实例,在某种情况下,它是有用的。为了实现这一目标,Swift提供了两种特殊的运算符:
l 相同(===
l 不相同的(!==
使用这些运算符来检查,两个常量或变量是否引用相同的单个实例:
if tenEighty === alsoTenEighty {
    println("tenEightyand alsoTenEighty refer to the same Resolution instance.")
}
// prints "tenEighty and alsoTenEighty refer to the sameResolution instance."

需要注意的是“相同的”(表示三个等号,或===),并不意味着同样的事情为“等于”(由两个等号,或者==表示):
l “相同”是指两个常数或类类型的变量指向完全相同的类的实例。
l “等于”是指两个实例被认为是价值“等于”或“等效”,对于“平等”的一些适当的意思,由类型的设计师所定义的。
当定义自己的自定义类和结构,自己的责任,在于以决定什么资格作为两个实例是“平等”。定义自己的实现过程中“等于”和运算符。
9.4.2 指针
如果有C语言的经验,C + +Objective-C中,可能知道,这些语言使用指针来引用在内存中的地址。Swift中的常量或变量,指的是一些引用类型的一个实例,类似于C中的指针,但不是直接指向内存中的地址,并且不要求写一个星号(*),表示正在创建一个引用。相反,这些文献被定义像任何其他常量或变量的Swift
9.5 选择类和结构之间
可以使用类和结构定义自来定义数据类型,用来作为程序代码的构建块。然而,结构的实例都是按值传递的,类的实例都是通过引用传递的。这意味着,它们适合于各种不同的任务。当考虑项目需要数据结构和功能时,就需要考虑每项目中的每个数据结构是否应定义为类或结构。
作为一般指南,在创建一个结构中的一个或更多,请考虑的这些条件的申请:
l 该结构的主要作用是封装一些相对简单的数据值。
l 当赋值或传送结构实例时,封装的值将被拷贝,而不是用来引用
l 结构的任何属性本身都是值类型,并且这些属性只用来被复制而不是用来引用。
l 结构并不需要继承自其他现有类型的属性或行为。
结构良好的应征人员(candidates)的例子包括:
l 几何形状的大小,也许是封装一个width属性和height属性,这两个属性采用Double类型。
l 在一系列范围内,一种方法是指,或许封装一start属性和length属性,这两个属性采用Int类型。
l 在三维坐标系统中的点,也许封装的XYZ的性质,每个类型都是Double类型。
在所有其他情况下,定义一个类,并创建进行管理和通过引用传递的类的实例。在实践中,这意味着大多数的自定义数据结构应该是类,而不是结构。。
9.6 集合的赋值和复制行为

Swift的数组和字典都是作为结构来实现的。在赋值常量和变量上,或者用来传递给函数或者方法上,数组略有与字典和其他结构不同的复制行为。
下面描述了数组和字典在在函数里的不同区别,其作为类实现,而不是结构。数组和字典实例,通常用来赋值或者作为现有实例的引用来传递,而不是作为一个副本。

下面描述了数组、字典、字符串和其他值的复制引用。凡是在的代码中所看到的的复制行为,将会永远把它作为复制的发生。在幕后,只有在绝对必要去复制时,Swift才会实际执行复制的操作。Swift管理着所有值的复制,来确保最佳的性能,故此不应该避免试图抢占这个优化赋值。
9.6.1 字典的赋值和复制行为
每当你赋值一个字典实例常量或变量时,或通过一个字典实例作为参数传递给函数或方法调用时,字典在赋值和调用行为发生同一时刻,字典也被复制。在结构中,这个过程将会描述到。
如果在字典实例存储的键/值是值类型(结构或枚举),在赋值或者调用发生的同时,它们竟会发生复制。相反,如果键/值是引用类型(类或函数),那么引用将会被复制,而不是指的是类的实例和函数。这种字典的键/值的复制行为,与结构被复制时,对应的结构的储存属性的复制行为是一样的。
下面的例子定义了一个名为ages的字典,它存储的四个人的namesages。该ages的字典,然后被赋值一个名为copiedAges的新变量,当这个赋值发生于此同时,copiedAges被复制。赋值后,agescopiedages将成为两单独的字典。
var ages = ["Peter": 23, "Wei": 35,"Anish": 65, "Katya": 19]
var copiedAges = ages

本字典中的键是String类型,而值类型为int。这两种类型都是Swift值类型,所以当字典复制发生,键/值也会被复制。
改变字典里的一个年龄值和检查其他里的相应的值,都会促使复制年龄字典的发生。如果将copiedAges字典里的“Peter”的值设置为24,这个年龄字典仍会从之前的返回23的老值:
copiedAges["Peter"] = 24
println(ages["Peter"])
// prints "23"

9.6.2 数组的赋值和复制行为
Swift中,与字典相比,数组的赋值和复制操作显得更为复杂。在使用数组的内容时,数组提供类似C的性能。只有当复制是必要时,才该复制数组的内容。
如果一个数组实例赋值给一个常量或变量,或将数组实例作为函数或者方法的参数以供调用,那么在赋值或者调用的与此同时,数组的内容将不会被复制。相反,两个数组将共享元素值的相同序列。当通过修改数组,修改其元素值时,通过其他的方式。其结果是可观察的。
对于数组而言,只会在执行可能修改数组的长度操作时,复制才会发生。这包括添加、插入、取出items,或用范围内的下标更换一个范围的数组中的items。如果当数组复制确实发生时,与字典的键值相比,其内容复制行为是一样的。。
下面的示例将Int值的新数组赋值一个名为a的变量。该数组被赋值到另外两个变量称为bc
var a = [1, 2, 3]
var b = a
var c = a

可以用下标检法索数组 a b c中的第一个值:
println(a[0])
// 1
println(b[0])
// 1
println(c[0])
// 1

如果使用下标法来设置数组中一项新值,那么a b c 三个也将都会返回新值。请注意,当用下标法设定新值于数组时,数组是不可复制的,因为用下标法设置单个值,没有可能的改变数组的长度:
a[0] = 42
println(a[0])
// 42
println(b[0])
// 42
println(c[0])
// 42

但是,如果追加一个新的项目到数组中,就得来修改数组的长度。在追加新值,Swift将会这提示来创建数组的新副本。因而说,数组是单独的独立的副本。
如果改变一个值的一个副本后,将返回从BC的一个不同的值,这两个仍引用从之前的复制发生在原数组内容不同的值:
a.append(4)
a[0] = 777
println(a[0])
// 777
println(b[0])
// 42
println(c[0])
// 42

9.6.3 确保数值是唯一的
执行数组的内容的行动之前,或者把数组传递给函数或者方法之前,它可以确保该数组会有一个唯一性的副本。通过调用数组中的变量上非共享方法,确保这个数组引用的唯一性。
如果多个变量,目前指向同一个数组,并调用这些变量之一的非共享的方法,该数组被复制,从而使变量拥有自己独立的数组副本。然而,如果该变量已经是该数组的唯一引用。
那么就不会发生复制性操作。
在前面的例子末尾中,,bc都引用相同的数组。调用b上非共享的方法,将会使之成为一个独特的副本:
b.unshare()

如果在调用非共享的方法之后,更改b中的第一个值,所有三个数组将会出现不同的值:
b[0] = -105
println(a[0])
// 777
println(b[0])
// -105
println(c[0])
// 42

9.6.4 检查两个数组是否共享相同的元素
通过使用恒等式运算符(= = =和!= =)比较它们,检查两个数组或子数组是否共享相同的存储所和元素。
下面的示例使用“恒等”运算符(= = =),检查BC是否仍然共享相同的数组元素::
if b === c {
    println("b and cstill share the same array elements.")
} else {
    println("b and c nowrefer to two independent sets of array elements.")
}
// prints "b and c now refer to two independent sets of arrayelements."

或者,使用恒等式,检查2子数组是否共享相同的元素。下面的例子比较了从B两个相同的子数组,并确认它们指的是同一个元素:
if b === c {
    println("b and cstill share the same array elements.")
} else {
    println("b and c nowrefer to two independent sets of array elements.")
}
// prints "b and c now refer to two independent sets of arrayelements."

9.6.5 强制性复制数组
通过调用数组的复制方法来强制显式复制数组。此方法执行数组的一个浅表副本,并返回一个包含复制的项目一个新的数组。
下面的例子定义了一个名为names的数组,其中存储了七个人的名字。一个名为copiedNames的新变量,用于设置调用同数组上的复制方法的结果:
var names = ["Mohsen", "Hilary","Justyn", "Amy", "Rich", "Graham","Vic"]
var copiedNames = names.copy()

通过改变数组中的某一项和检查其他相应的项目,来实现复制 names数值。把copiedNames数组中的第一项设置“Mo”,而非“Mohsen”,names数组仍将从之前的副本返回旧值“Mohsen”:
copiedNames[0] = "Mo"
println(names[0])
// prints "Mohsen"



如果只需要确保引用一个数组的内容是现存的唯一引用,应调用非共享方法,而不是调用复制方法。非共享方法并不能使产生出数组的副本,除非它是必要这么做。复制方法总是复制数组,哪怕是已经取消共享。



10属性

11方法

12下标

13继承

14初始化

15清除

16 自动引用计数

17可选链接

18类型转换

19嵌套类型

20扩展

21协议

22 泛型

23高级运算符号


[ 此帖被yifeilang123在2014-06-10 00:15重新编辑 ]

清空我的评分动态本帖最近评分记录: 共2条评分记录
angellixf 可可豆 +5 2014-06-06 -
lyywhg 可可豆 +10 2014-06-05 -
隐藏评分记录
关键词: swift
级别: *
UID: 213284
精华: *
发帖: *
可可豆: * CB
威望: * 点
在线时间: (时)
注册时间: *
最后登录: *
1 楼:  发表于: 2014-06-05 08:30    发自: Web Page
iOS 7:iPhone/iPad应用开发技术详解
级别: 版主

状态: 连续签到 - [4天]
UID: 301302
精华: 0
发帖: 1723
可可豆: 1535 CB
威望: 2482 点
在线时间: 2797(时)
注册时间: 2014-03-19
最后登录: 2018-11-09
2 楼:  发表于: 2014-06-05 09:23    发自: Web Page
强势关注, 楼主要坚持翻译完啊。。辛苦了哇。。
级别: 版主

状态: 连续签到 - [392天]
UID: 152587
精华: 0
发帖: 2037
可可豆: 5057 CB
威望: 4403 点
在线时间: 1408(时)
注册时间: 2012-07-05
最后登录: 2018-03-30
3 楼:  发表于: 2014-06-05 09:43    发自: Web Page
英文好就是好
努力成为一枚合格的Coder.
级别: 新手上路
状态: 连续签到 - [1天]
UID: 296918
精华: 0
发帖: 8
可可豆: 132 CB
威望: 20 点
在线时间: 113(时)
注册时间: 2014-03-04
最后登录: 2014-09-03
4 楼:  发表于: 2014-06-05 11:53    发自: Web Page
mark 楼主好牛逼 我对照英文看了一下 翻译很是到位   攒!!
级别: 新手上路
UID: 94181
精华: 0
发帖: 26
可可豆: 103 CB
威望: 143 点
在线时间: 100(时)
注册时间: 2011-09-20
最后登录: 2016-03-11
5 楼:  发表于: 2014-06-05 12:11    发自: Web Page
mark一下,顶楼主,希望楼主坚持写下去
级别: 新手上路
UID: 265658
精华: 0
发帖: 1
可可豆: 1 CB
威望: 1 点
在线时间: 56(时)
注册时间: 2013-10-14
最后登录: 2018-01-04
6 楼:  发表于: 2014-06-05 13:00    发自: Web Page

级别: 新手上路

状态: 连续签到 - [4天]
UID: 150153
精华: 0
发帖: 43
可可豆: 134 CB
威望: 83 点
在线时间: 304(时)
注册时间: 2012-06-21
最后登录: 2017-06-20
7 楼:  发表于: 2014-06-05 13:17    发自: Web Page
支持,楼主继续加油。
级别: 侠客
状态: 连续签到 - [7天]
UID: 298770
精华: 0
发帖: 65
可可豆: 338 CB
威望: 301 点
在线时间: 286(时)
注册时间: 2014-03-10
最后登录: 2018-11-21
8 楼:  发表于: 2014-06-05 14:25    发自: Web Page
顶。楼主坚持翻译完,直接出本书。
谢谢了!

级别: 新手上路
UID: 304036
精华: 0
发帖: 2
可可豆: 2 CB
威望: 2 点
在线时间: 345(时)
注册时间: 2014-03-29
最后登录: 2016-08-17
9 楼:  发表于: 2014-06-05 14:44    发自: Web Page
终于等到了 感谢翻译  希望能点评下Swift 优缺点

本站内容均为本站转发,已尽可能注明出处。因未能核实来源或转发内容图片有权利瑕疵的,请及时联系本站,本站会第一时间进行修改或删除。 QQ : 3442093904

描述
快速回复

关注本帖(如果有新回复会站内信通知您)

发帖、回帖都会得到可观的积分奖励。查看论坛积分规则

按"Ctrl+Enter"直接提交
    顶部