Merge branch 'numbbbbb/gh-pages' into develop
This commit is contained in:
@ -301,7 +301,7 @@ func lessThanTen(number: Int) -> Bool {
|
||||
return number < 10
|
||||
}
|
||||
var numbers = [20, 19, 7, 12]
|
||||
hasAnyMatches(numbers, lessThanTen)
|
||||
hasAnyMatches(numbers, condition: lessThanTen)
|
||||
```
|
||||
|
||||
函數實際上是一種特殊的閉包,你可以使用`{}`來創建一個匿名閉包。使用`in`將參數和返回值類型聲明與閉包涵數體進行分離。
|
||||
@ -649,7 +649,7 @@ let bDescription = b.simpleDescription
|
||||
|
||||
注意聲明`SimpleStructure`時候`mutating`關鍵字用來標記一個會修改結構體的方法。`SimpleClass`的聲明不需要標記任何方法因為類中的方法經常會修改類。
|
||||
|
||||
使用`extension`來為現有的類型添加功能,比如新的方法和參數。你可以使用擴展來改造定義在別處,甚至是從外部庫或者框架引入的一個類型,使得這個類型遵循某個協議。
|
||||
使用`extension`來為現有的類型添加功能,比如新的方法和計算屬性。你可以使用擴展來改造定義在別處,甚至是從外部庫或者框架引入的一個類型,使得這個類型遵循某個協議。
|
||||
|
||||
```swift
|
||||
extension Int: ExampleProtocol {
|
||||
|
||||
@ -225,7 +225,7 @@ ovenLight.next()
|
||||
|
||||
```swift
|
||||
class SomeClass {
|
||||
class func someTypeMethod() {
|
||||
static func someTypeMethod() {
|
||||
// type method implementation goes here
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,10 +22,10 @@
|
||||
通常来说,编程语言教程中的第一个程序应该在屏幕上打印“Hello, world”。在 Swift 中,可以用一行代码实现:
|
||||
|
||||
```swift
|
||||
print("Hello, world")
|
||||
print("Hello, world!")
|
||||
```
|
||||
|
||||
如果你写过 C 或者 Objective-C 代码,那你应该很熟悉这种形式——在 Swift 中,这行代码就是一个完整的程序。你不需要为了输入输出或者字符串处理导入一个单独的库。全局作用域中的代码会被自动当做程序的入口点,所以你也不需要`main`函数。你同样不需要在每个语句结尾写上分号。
|
||||
如果你写过 C 或者 Objective-C 代码,那你应该很熟悉这种形式——在 Swift 中,这行代码就是一个完整的程序。你不需要为了输入输出或者字符串处理导入一个单独的库。全局作用域中的代码会被自动当做程序的入口点,所以你也不需要`main()`函数。你同样不需要在每个语句结尾写上分号。
|
||||
|
||||
这个教程会通过一系列编程例子来让你对 Swift 有初步了解,如果你有什么不理解的地方也不用担心——任何本章介绍的内容都会在后面的章节中详细讲解。
|
||||
|
||||
@ -79,14 +79,12 @@ let fruitSummary = "I have \(apples + oranges) pieces of fruit."
|
||||
> 练习:
|
||||
> 使用`\()`来把一个浮点计算转换成字符串,并加上某人的名字,和他打个招呼。
|
||||
|
||||
使用方括号`[]`来创建数组和字典,并使用下标或者键(key)来访问元素。
|
||||
使用方括号`[]`来创建数组和字典,并使用下标或者键(key)来访问元素。最后一个元素后面允许有个逗号。
|
||||
|
||||
```swift
|
||||
var shoppingList = ["catfish", "water", "tulips", "blue paint"]
|
||||
shoppingList[1] = "bottle of water"
|
||||
```
|
||||
|
||||
```swift
|
||||
var occupations = [
|
||||
"Malcolm": "Captain",
|
||||
"Kaylee": "Mechanic",
|
||||
@ -152,20 +150,21 @@ if let name = optionalName {
|
||||
let vegetable = "red pepper"
|
||||
switch vegetable {
|
||||
case "celery":
|
||||
let vegetableComment = "Add some raisins and make ants on a log."
|
||||
print("Add some raisins and make ants on a log.")
|
||||
case "cucumber", "watercress":
|
||||
let vegetableComment = "That would make a good tea sandwich."
|
||||
print("That would make a good tea sandwich.")
|
||||
case let x where x.hasSuffix("pepper"):
|
||||
let vegetableComment = "Is it a spicy \(x)?"
|
||||
print("Is it a spicy \(x)?")
|
||||
default:
|
||||
let vegetableComment = "Everything tastes good in soup."
|
||||
print("Everything tastes good in soup.")
|
||||
}
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 删除`default`语句,看看会有什么错误?
|
||||
|
||||
声明`let`可用于匹配某部分固定值的模式
|
||||
|
||||
注意`let`在上述例子的等式中是如何使用的,它将匹配等式的值赋给常量`x`。
|
||||
|
||||
运行`switch`中匹配到的子句之后,程序会退出`switch`语句,并不会继续向下运行,所以不需要在每个子句结尾写`break`。
|
||||
|
||||
@ -323,7 +322,7 @@ func lessThanTen(number: Int) -> Bool {
|
||||
return number < 10
|
||||
}
|
||||
var numbers = [20, 19, 7, 12]
|
||||
hasAnyMatches(numbers, lessThanTen)
|
||||
hasAnyMatches(numbers, condition: lessThanTen)
|
||||
```
|
||||
|
||||
函数实际上是一种特殊的闭包:它是一段能之后被调取的代码。闭包中的代码能访问闭包所建作用域中能得到的变量和函数,即使闭包是在一个不同的作用域被执行的 - 你已经在嵌套函数例子中所看到。你可以使用`{}`来创建一个匿名闭包。使用`in`将参数和返回值类型声明与闭包函数体进行分离。
|
||||
@ -342,14 +341,14 @@ numbers.map({
|
||||
有很多种创建更简洁的闭包的方法。如果一个闭包的类型已知,比如作为一个回调函数,你可以忽略参数的类型和返回值。单个语句闭包会把它语句的值当做结果返回。
|
||||
|
||||
```swift
|
||||
let sortedNumbers = numbers.sort { $0 > $1 }
|
||||
print(sortedNumbers)
|
||||
let mappedNumbers = numbers.map({ number in 3 * number })
|
||||
print(mappedNumbers)
|
||||
```
|
||||
|
||||
你可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。当一个闭包是传给函数的唯一参数,你可以完全忽略括号。
|
||||
|
||||
```swift
|
||||
let sortedNumbers = sorted(numbers) { $0 > $1 }
|
||||
let sortedNumbers = numbers.sort { $0 > $1 }
|
||||
print(sortedNumbers)
|
||||
```
|
||||
|
||||
@ -442,12 +441,12 @@ class EquilateralTriangle: NamedShape {
|
||||
}
|
||||
|
||||
var perimeter: Double {
|
||||
get {
|
||||
return 3.0 * sideLength
|
||||
}
|
||||
set {
|
||||
sideLength = newValue / 3.0
|
||||
}
|
||||
get {
|
||||
return 3.0 * sideLength
|
||||
}
|
||||
set {
|
||||
sideLength = newValue / 3.0
|
||||
}
|
||||
}
|
||||
|
||||
override func simpleDescription() -> String {
|
||||
@ -475,14 +474,14 @@ print(triangle.sideLength)
|
||||
```swift
|
||||
class TriangleAndSquare {
|
||||
var triangle: EquilateralTriangle {
|
||||
willSet {
|
||||
square.sideLength = newValue.sideLength
|
||||
}
|
||||
willSet {
|
||||
square.sideLength = newValue.sideLength
|
||||
}
|
||||
}
|
||||
var square: Square {
|
||||
willSet {
|
||||
triangle.sideLength = newValue.sideLength
|
||||
}
|
||||
willSet {
|
||||
triangle.sideLength = newValue.sideLength
|
||||
}
|
||||
}
|
||||
init(size: Double, name: String) {
|
||||
square = Square(sideLength: size, name: name)
|
||||
@ -535,9 +534,9 @@ let aceRawValue = ace.rawValue
|
||||
> 练习:
|
||||
> 写一个函数,通过比较它们的原始值来比较两个`Rank`值。
|
||||
|
||||
在上面的例子中,枚举原始值的类型是`Int`,所以你只需要设置第一个原始值。剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。使用`rawValue`来访问一个枚举成员的原始值。
|
||||
在上面的例子中,枚举原始值的类型是`Int`,所以你只需要设置第一个原始值。剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。使用`rawValue`属性来访问一个枚举成员的原始值。
|
||||
|
||||
使用`init?(rawValue:)`构造器来从原始值中枚举一个例子。
|
||||
使用`init?(rawValue:)`初始化构造器在原始值和枚举值之间进行转换。
|
||||
|
||||
```swift
|
||||
if let convertedRank = Rank(rawValue: 3) {
|
||||
@ -562,7 +561,6 @@ enum Suit {
|
||||
return "clubs"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
let hearts = Suit.Hearts
|
||||
let heartsDescription = hearts.simpleDescription()
|
||||
@ -599,10 +597,10 @@ enum ServerResponse {
|
||||
case Result(String, String)
|
||||
case Error(String)
|
||||
}
|
||||
|
||||
|
||||
let success = ServerResponse.Result("6:00 am", "8:09 pm")
|
||||
let failure = ServerResponse.Error("Out of cheese.")
|
||||
|
||||
|
||||
switch success {
|
||||
case let .Result(sunrise, sunset):
|
||||
let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
|
||||
@ -656,14 +654,14 @@ let bDescription = b.simpleDescription
|
||||
> 练习:
|
||||
> 写一个实现这个协议的枚举。
|
||||
|
||||
注意声明`SimpleStructure`时候`mutating`关键字用来标记一个会修改结构体的方法。`SimpleClass`的声明不需要标记任何方法因为类中的方法经常会修改类。
|
||||
注意声明`SimpleStructure`时候`mutating`关键字用来标记一个会修改结构体的方法。`SimpleClass`的声明不需要标记任何方法,因为类中的方法通常可以修改类属性(类的性质)。
|
||||
|
||||
使用`extension`来为现有的类型添加功能,比如新的方法和参数。你可以使用扩展在别处修改定义,甚至是从外部库或者框架引入的一个类型,使得这个类型遵循某个协议。
|
||||
使用`extension`来为现有的类型添加功能,比如新的方法和计算属性。你可以使用扩展在别处修改定义,甚至是从外部库或者框架引入的一个类型,使得这个类型遵循某个协议。
|
||||
|
||||
```swift
|
||||
extension Int: ExampleProtocol {
|
||||
var simpleDescription: String {
|
||||
return "The number \(self)"
|
||||
return "The number \(self)"
|
||||
}
|
||||
mutating func adjust() {
|
||||
self += 42
|
||||
@ -679,8 +677,8 @@ print(7.simpleDescription)
|
||||
|
||||
```swift
|
||||
let protocolValue: ExampleProtocol = a
|
||||
protocolValue.simpleDescription
|
||||
// protocolValue.anotherProperty // Uncomment to see the error
|
||||
print(protocolValue.simpleDescription)
|
||||
// print(protocolValue.anotherProperty) // Uncomment to see the error
|
||||
```
|
||||
|
||||
即使`protocolValue`变量运行时的类型是`simpleClass`,编译器会把它的类型当做`ExampleProtocol`。这表示你不能调用类在它实现的协议之外实现的方法或者属性。
|
||||
@ -705,18 +703,18 @@ repeatItem("knock", numberOfTimes:4)
|
||||
|
||||
```swift
|
||||
// Reimplement the Swift standard library's optional type
|
||||
enum OptionalValue<T> {
|
||||
enum OptionalValue<Wrapped> {
|
||||
case None
|
||||
case Some(T)
|
||||
case Some(Wrapped)
|
||||
}
|
||||
var possibleInteger: OptionalValue<Int> = .None
|
||||
possibleInteger = .Some(100)
|
||||
```
|
||||
|
||||
在类型名后面使用`where`来指定对类型的需求,比如,限定类型实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类
|
||||
在类型名后面使用`where`来指定对类型的需求,比如,限定类型实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类。
|
||||
|
||||
```swift
|
||||
func anyCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, _ rhs: U) -> Bool {
|
||||
func anyCommonElements <T: SequenceType, U: SequenceType where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, _ rhs: U) -> Bool {
|
||||
for lhsItem in lhs {
|
||||
for rhsItem in rhs {
|
||||
if lhsItem == rhsItem {
|
||||
@ -732,4 +730,4 @@ anyCommonElements([1, 2, 3], [3])
|
||||
> 练习:
|
||||
> 修改`anyCommonElements(_:_:)`函数来创建一个函数,返回一个数组,内容是两个序列的共有元素。
|
||||
|
||||
简单起见,你可以忽略`where`,只在冒号后面写协议或者类名。` <T: Equatable>`和`<T where T: Equatable>`是等价的。
|
||||
` <T: Equatable>`和`<T where T: Equatable>`是等价的。
|
||||
|
||||
@ -149,23 +149,14 @@ languageName = "Swift++"
|
||||
<a name="printing"></a>
|
||||
### 输出常量和变量
|
||||
|
||||
你可以用`print(_:)`函数来输出当前常量或变量的值:
|
||||
你可以用`print(_:separator:terminator:)`函数来输出当前常量或变量的值:
|
||||
|
||||
```swift
|
||||
print(friendlyWelcome)
|
||||
// 输出 "Bonjour!"
|
||||
```
|
||||
|
||||
`print(_:)`是一个用来输出的全局函数,输出的内容会在最后换行。如果你用 Xcode,`print(_:)`将会输出内容到“console”面板上。(另一种函数叫`print(_:appendNewline:)`,唯一区别是在输出内容最后不会换行。)
|
||||
|
||||
`print(_:)`函数输出传入的`String`值:
|
||||
|
||||
```swift
|
||||
print("This is a string")
|
||||
// 输出 "This is a string"
|
||||
```
|
||||
|
||||
`print(_:)`函数可以输出更复杂的信息。这些信息可以包含当前常量和变量的值。
|
||||
`print(_:separator:terminator:)`是一个用来输出一个或多个值到适当输出区的全局函数。如果你用 Xcode,`print(_:separator:terminator:)`将会输出内容到“console”面板上。`separator`和`terminator`参数具有默认值,因此你调用这个函数的时候可以忽略它们。默认情况下,该函数通过添加换行符来结束当前行。如果不想换行,可以传递一个空字符串给`terminator`参数--例如,`print(someValue, terminator:"")`。关于参数默认值的更多信息,请参考[默认参数值](./06_Functions.html#default_parameter_values)。
|
||||
|
||||
Swift 用字符串插值(string interpolation)的方式把常量名或者变量名当做占位符加入到长字符串中,Swift 会用当前常量或变量的值替换这些占位符。将常量或变量名放入圆括号中,并在开括号前使用反斜杠将其转义:
|
||||
|
||||
@ -223,7 +214,7 @@ Swift 提供了8,16,32和64位的有符号和无符号整数类型。这些
|
||||
<a name="integer_bounds"></a>
|
||||
### 整数范围
|
||||
|
||||
你可以访问不同整数类型的`min`和`max`属性来获取对应类型的最大值和最小值:
|
||||
你可以访问不同整数类型的`min`和`max`属性来获取对应类型的最小值和最大值:
|
||||
|
||||
```swift
|
||||
let minValue = UInt8.min // minValue 为 0,是 UInt8 类型
|
||||
@ -239,7 +230,7 @@ let maxValue = UInt8.max // maxValue 为 255,是 UInt8 类型
|
||||
* 在32位平台上,`Int`和`Int32`长度相同。
|
||||
* 在64位平台上,`Int`和`Int64`长度相同。
|
||||
|
||||
除非你需要特定长度的整数,一般来说使用`Int`就够了。这可以提高代码一致性和可复用性。即使是在32位平台上,`Int`可以存储的整数范围也可以达到`-2147483648`~`2147483647`,大多数时候这已经足够大了。
|
||||
除非你需要特定长度的整数,一般来说使用`Int`就够了。这可以提高代码一致性和可复用性。即使是在32位平台上,`Int`可以存储的整数范围也可以达到`-2,147,483,648`~`2,147,483,647`,大多数时候这已经足够大了。
|
||||
|
||||
<a name="UInt"></a>
|
||||
### UInt
|
||||
@ -272,7 +263,7 @@ Swift 是一个类型安全(type safe)的语言。类型安全的语言可
|
||||
|
||||
由于 Swift 是类型安全的,所以它会在编译你的代码时进行类型检查(type checks),并把不匹配的类型标记为错误。这可以让你在开发的时候尽早发现并修复错误。
|
||||
|
||||
当你要处理不同类型的值时,类型检查可以帮你避免错误。然而,这并不是说你每次声明常量和变量的时候都需要显式指定类型。如果你没有显式指定类型,Swift 会使用_类型推断(type inference)来选择合适的类型。有了类型推断,编译器可以在编译代码的时候自动推断出表达式的类型。原理很简单,只要检查你赋的值即可。
|
||||
当你要处理不同类型的值时,类型检查可以帮你避免错误。然而,这并不是说你每次声明常量和变量的时候都需要显式指定类型。如果你没有显式指定类型,Swift 会使用_类型推断_(type inference)来选择合适的类型。有了类型推断,编译器可以在编译代码的时候自动推断出表达式的类型。原理很简单,只要检查你赋的值即可。
|
||||
|
||||
因为有类型推断,和 C 或者 Objective-C 比起来 Swift 很少需要声明类型。常量和变量虽然需要明确类型,但是大部分工作并不需要你自己来完成。
|
||||
|
||||
@ -430,7 +421,7 @@ var maxAmplitudeFound = AudioSample.min
|
||||
<a name="booleans"></a>
|
||||
## 布尔值
|
||||
|
||||
Swift 有一个基本的布尔(Boolean)类型,叫做`Bool`。布尔值指逻辑上的(logical,因为它们只能是真或者假。Swift 有两个布尔常量,`true`和`false`:
|
||||
Swift 有一个基本的布尔(Boolean)类型,叫做`Bool`。布尔值指逻辑上的值,因为它们只能是真或者假。Swift 有两个布尔常量,`true`和`false`:
|
||||
|
||||
```swift
|
||||
let orangesAreOrange = true
|
||||
@ -551,17 +542,17 @@ print("The status message is \(http200Status.description)")
|
||||
> 注意:
|
||||
C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回`nil`,`nil`表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型,Objective-C 方法一般会返回一个特殊值(比如`NSNotFound`)来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而,Swift 的可选类型可以让你暗示_任意类型_的值缺失,并不需要一个特殊值。
|
||||
|
||||
来看一个例子。Swift 的`String`类型有一个叫做`toInt`的方法,作用是将一个`String`值转换成一个`Int`值。然而,并不是所有的字符串都可以转换成一个整数。字符串`"123"`可以被转换成数字`123`,但是字符串`"hello, world"`不行。
|
||||
来看一个例子。Swift 的`String`类型有一种构造器,作用是将一个`String`值转换成一个`Int`值。然而,并不是所有的字符串都可以转换成一个整数。字符串`"123"`可以被转换成数字`123`,但是字符串`"hello, world"`不行。
|
||||
|
||||
下面的例子使用`toInt`方法来尝试将一个`String`转换成`Int`:
|
||||
下面的例子使用这种构造器来尝试将一个`String`转换成`Int`:
|
||||
|
||||
```swift
|
||||
let possibleNumber = "123"
|
||||
let convertedNumber = possibleNumber.toInt()
|
||||
let convertedNumber = Int(possibleNumber)
|
||||
// convertedNumber 被推测为类型 "Int?", 或者类型 "optional Int"
|
||||
```
|
||||
|
||||
因为`toInt`方法可能会失败,所以它返回一个_可选类型(optional)`Int`,而不是一个`Int`。一个可选的`Int`被写作`Int?`而不是`Int`。问号暗示包含的值是可选类型,也就是说可能包含`Int`值也可能不包含值。(不能包含其他任何值比如`Bool`值或者`String`值。只能是`Int`或者什么都没有。)
|
||||
因为该构造器可能会失败,所以它返回一个_可选类型_(optional)`Int`,而不是一个`Int`。一个可选的`Int`被写作`Int?`而不是`Int`。问号暗示包含的值是可选类型,也就是说可能包含`Int`值也可能不包含值。(不能包含其他任何值比如`Bool`值或者`String`值。只能是`Int`或者什么都没有。)
|
||||
|
||||
<a name="nil"></a>
|
||||
### nil
|
||||
@ -591,9 +582,9 @@ Swift 的`nil`和 Objective-C 中的`nil`并不一样。在 Objective-C 中,`n
|
||||
<a name="if"></a>
|
||||
### if 语句以及强制解析
|
||||
|
||||
你可以使用`if`语句和nil比较来判断一个可选值是否包含值。你可以使用“相等”(==)或“不等”(!=)来执行比较。
|
||||
你可以使用`if`语句和`nil`比较来判断一个可选值是否包含值。你可以使用“相等”(`==`)或“不等”(`!=`)来执行比较。
|
||||
|
||||
如果可选类型有值,它将不等于nil:
|
||||
如果可选类型有值,它将不等于`nil`:
|
||||
|
||||
```swift
|
||||
if convertedNumber != nil {
|
||||
@ -601,7 +592,7 @@ if convertedNumber != nil {
|
||||
}
|
||||
// 输出 "convertedNumber contains some integer value."
|
||||
```
|
||||
当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(`!`)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的_强制解析(forced unwrapping):
|
||||
当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(`!`)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的_强制解析_(forced unwrapping):
|
||||
|
||||
```swift
|
||||
if convertedNumber != nil {
|
||||
@ -641,18 +632,19 @@ if let actualNumber = Int(possibleNumber) {
|
||||
|
||||
这段代码可以被理解为:
|
||||
|
||||
“如果`possibleNumber.toInt`返回的可选`Int`包含一个值,创建一个叫做`actualNumber`的新常量并将可选包含的值赋给它。”
|
||||
“如果`Int(possibleNumber)`返回的可选`Int`包含一个值,创建一个叫做`actualNumber`的新常量并将可选包含的值赋给它。”
|
||||
|
||||
如果转换成功,`actualNumber`常量可以在`if`语句的第一个分支中使用。它已经被可选类型_包含的_值初始化过,所以不需要再使用`!`后缀来获取它的值。在这个例子中,`actualNumber`只被用来输出转换结果。
|
||||
|
||||
你可以在可选绑定中使用常量和变量。如果你想在`if`语句的第一个分支中操作`actualNumber`的值,你可以改成`if var actualNumber`,这样可选类型包含的值就会被赋给一个变量而非常量。
|
||||
|
||||
多个可选绑定可以用逗号区分成一列表达式出现在一个`if`语句中。
|
||||
你可以包含多个可选绑定在`if`语句中,并使用`where`子句做布尔值判断。
|
||||
|
||||
```swift
|
||||
if let constantName = someOptional, anotherConstantName = someOtherOptional {
|
||||
statements
|
||||
if let firstNumber = Int("4"), secondNumber = Int("42") where firstNumber < secondNumber {
|
||||
print("\(firstNumber) < \(secondNumber)")
|
||||
}
|
||||
// prints "4 < 42"
|
||||
```
|
||||
|
||||
<a name="implicityly_unwrapped_optionals"></a>
|
||||
@ -704,15 +696,15 @@ if let definiteString = assumedString {
|
||||
|
||||
<a name="error_handling"></a>
|
||||
## 错误处理
|
||||
你可以使用错误处理(error handling)来应对程序执行中出错的条件。
|
||||
你可以使用错误处理(error handling)来应对程序执行中可能会遇到的错误条件。
|
||||
|
||||
相对于可选中运用值得存在与缺失来表达函数的成功与失败,错误处理可以推断失败的原因,并传送至程序的其他部分。
|
||||
相对于可选中运用值的存在与缺失来表达函数的成功与失败,错误处理可以推断失败的原因,并传播至程序的其他部分。
|
||||
|
||||
当一个函数遇到错误条件,它能报错。调用函数的地方能抛出错误消息并合理处理。
|
||||
|
||||
```swift
|
||||
func canThrowAnErrow() throws{
|
||||
//this function may or may not throw an error
|
||||
func canThrowAnError() throws {
|
||||
// 这个函数有可能抛出错误
|
||||
}
|
||||
```
|
||||
|
||||
@ -720,37 +712,37 @@ func canThrowAnErrow() throws{
|
||||
|
||||
```swift
|
||||
do {
|
||||
try canThrowAnErrow()
|
||||
try canThrowAnError()
|
||||
// 没有错误消息抛出
|
||||
} catch {
|
||||
// 有一个错误消息抛出
|
||||
}
|
||||
```
|
||||
|
||||
一个`do`的声明创建了一个新的包含作用域,使得错误能被传播到一个或更多`catch`从句。
|
||||
一个`do`语句创建了一个新的包含作用域,使得错误能被传播到一个或多个`catch`从句。
|
||||
|
||||
这里有一个错误处理用来应对不同错误条件的例子。
|
||||
这里有一个错误处理如何用来应对不同错误条件的例子。
|
||||
|
||||
```swift
|
||||
func makeASandwich() throws {
|
||||
// ...
|
||||
// ...
|
||||
}
|
||||
|
||||
do {
|
||||
try makeASandwich()
|
||||
eatASandwich()
|
||||
} catch Error.OutOfCleanDishes{
|
||||
washDishes()
|
||||
try makeASandwich()
|
||||
eatASandwich()
|
||||
} catch Error.OutOfCleanDishes {
|
||||
washDishes()
|
||||
} catch Error.MissingIngredients(let ingredients) {
|
||||
buyGroceries(ingredients)
|
||||
buyGroceries(ingredients)
|
||||
}
|
||||
```
|
||||
|
||||
在此例中,`makeASandwich()`(做一个三明治)函数会抛出一个错误消息如果没有干净的盘子或者某个原料缺失。因为`makeASandwich()`抛出错误,调用函数会被包裹在`try`表达式中。将函数包裹在一个`do`声明中,任何被抛出的错误会被传送到提供的`catch`从句中。
|
||||
在此例中,`makeASandwich()`(做一个三明治)函数会抛出一个错误消息如果没有干净的盘子或者某个原料缺失。因为`makeASandwich()`抛出错误,函数调用被包裹在`try`表达式中。将函数包裹在一个`do`语句中,任何被抛出的错误会被传播到提供的`catch`从句中。
|
||||
|
||||
如果没有错误被抛出, `eatASandwich()`函数会被调用。如果一个符合`Error.OutOfCleanDishes`的错误被抛出,`washDishes`函数会被调用。如果一个符合`Error.MissingIngredients`的错误被抛出,`buyGroceries(_:)`函数会被调用并传递相关被`catch`所捕捉到的`[String]`值。
|
||||
如果没有错误被抛出, `eatASandwich()`函数会被调用。如果一个匹配`Error.OutOfCleanDishes`的错误被抛出,`washDishes`函数会被调用。如果一个匹配`Error.MissingIngredients`的错误被抛出,`buyGroceries(_:)`函数会随着被`catch`所捕捉到的关联值`[String]`被调用。
|
||||
|
||||
抛出,捕捉,传递错误会在[错误处理](./18_Error_Handling.html)章节详细说明。
|
||||
抛出,捕捉,以及传播错误会在[错误处理](./18_Error_Handling.html)章节详细说明。
|
||||
|
||||
<a name="assertions"></a>
|
||||
## 断言
|
||||
@ -759,11 +751,11 @@ do {
|
||||
|
||||
### 使用断言进行调试
|
||||
|
||||
断言会在运行时判断一个逻辑条件是否为`true`。从字面意思来说,断言“断言”一个条件是否为真。你可以使用断言来保证在运行其他代码之前,某些重要的条件已经被满足。如果条件判断为`true`,代码运行会继续进行;如果条件判断为`false`,代码运行停止,你的应用被终止。
|
||||
断言会在运行时判断一个逻辑条件是否为`true`。从字面意思来说,断言“断言”一个条件是否为真。你可以使用断言来保证在运行其他代码之前,某些重要的条件已经被满足。如果条件判断为`true`,代码运行会继续进行;如果条件判断为`false`,代码执行结束,你的应用被终止。
|
||||
|
||||
如果你的代码在调试环境下触发了一个断言,比如你在 Xcode 中构建并运行一个应用,你可以清楚地看到不合法的状态发生在哪里并检查断言被触发时你的应用的状态。此外,断言允许你附加一条调试信息。
|
||||
|
||||
你可以使用全局`assert`函数来写一个断言。向`assert`函数传入一个结果为`true`或者`false`的表达式以及一条信息,当表达式为`false`的时候这条信息会被显示:
|
||||
你可以使用全局`assert(_:_:)`函数来写一个断言。向`assert(_:_:)`函数传入一个结果为`true`或者`false`的表达式以及一条信息,当表达式的结果为`false`的时候这条信息会被显示:
|
||||
|
||||
```swift
|
||||
let age = -3
|
||||
@ -771,9 +763,9 @@ assert(age >= 0, "A person's age cannot be less than zero")
|
||||
// 因为 age < 0,所以断言会触发
|
||||
```
|
||||
|
||||
在这个例子中,只有`age >= 0`为`true`的时候,即`age`的值非负的时候,代码运行才会继续。如果`age`的值是负数,就像代码中那样,`age >= 0`为`false`,断言被触发,结束应用。
|
||||
在这个例子中,只有`age >= 0`为`true`的时候,即`age`的值非负的时候,代码才会继续执行。如果`age`的值是负数,就像代码中那样,`age >= 0`为`false`,断言被触发,终止应用。
|
||||
|
||||
断言信息如果不需要,可以被省略,就像这样:
|
||||
如果不需要断言信息,可以省略,就像这样:
|
||||
|
||||
```swift
|
||||
assert(age >= 0)
|
||||
|
||||
@ -44,22 +44,27 @@ Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减
|
||||
|
||||
赋值运算(`a = b`),表示用`b`的值来初始化或更新`a`的值:
|
||||
|
||||
let b = 10
|
||||
var a = 5
|
||||
a = b
|
||||
// a 现在等于 10
|
||||
```swift
|
||||
let b = 10
|
||||
var a = 5
|
||||
a = b
|
||||
// a 现在等于 10
|
||||
```
|
||||
|
||||
如果赋值的右边是一个多元组,它的元素可以马上被分解成多个常量或变量:
|
||||
|
||||
let (x, y) = (1, 2)
|
||||
// 现在 x 等于 1, y 等于 2
|
||||
```swift
|
||||
let (x, y) = (1, 2)
|
||||
// 现在 x 等于 1, y 等于 2
|
||||
```
|
||||
|
||||
与 C 语言和 Objective-C 不同,Swift 的赋值操作并不返回任何值。所以以下代码是错误的:
|
||||
|
||||
if x = y {
|
||||
// 此句错误, 因为 x = y 并不返回任何值
|
||||
}
|
||||
|
||||
```swift
|
||||
if x = y {
|
||||
// 此句错误, 因为 x = y 并不返回任何值
|
||||
}
|
||||
```
|
||||
|
||||
这个特性使你无法把(`==`)错写成(`=`),由于`if x = y`是错误代码,Swift帮你避免此类错误的的发生。
|
||||
|
||||
@ -73,18 +78,20 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
- 乘法(`*`)
|
||||
- 除法(`/`)
|
||||
|
||||
1 + 2 // 等于 3
|
||||
5 - 3 // 等于 2
|
||||
2 * 3 // 等于 6
|
||||
10.0 / 2.5 // 等于 4.0
|
||||
|
||||
|
||||
```swift
|
||||
1 + 2 // 等于 3
|
||||
5 - 3 // 等于 2
|
||||
2 * 3 // 等于 6
|
||||
10.0 / 2.5 // 等于 4.0
|
||||
```
|
||||
|
||||
与 C 语言和 Objective-C 不同的是,Swift 默认情况下不允许在数值运算中出现溢出情况。但是你可以使用 Swift 的溢出运算符来实现溢出运算(如`a &+ b`)。详情参见[溢出运算符](./24_Advanced_Operators.html#overflow_operators)。
|
||||
|
||||
加法运算符也可用于`String`的拼接:
|
||||
|
||||
"hello, " + "world" // 等于 "hello, world"
|
||||
```swift
|
||||
"hello, " + "world" // 等于 "hello, world"
|
||||
```
|
||||
|
||||
### 求余运算符
|
||||
|
||||
@ -101,8 +108,9 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
|
||||
在 Swift 中可以表达为:
|
||||
|
||||
9 % 4 // 等于 1
|
||||
|
||||
```swift
|
||||
9 % 4 // 等于 1
|
||||
```
|
||||
|
||||
为了得到`a % b`的结果,`%`计算了以下等式,并输出`余数`作为结果:
|
||||
|
||||
@ -114,10 +122,11 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
|
||||
9 = (4 × 2) + 1
|
||||
|
||||
同样的方法,我们来计算 `-9 % 4`:
|
||||
|
||||
同样的方法,我来们计算 `-9 % 4`:
|
||||
|
||||
-9 % 4 // 等于 -1
|
||||
```swift
|
||||
-9 % 4 // 等于 -1
|
||||
```
|
||||
|
||||
把`-9`和`4`代入等式,`-2`是取到的最大整数:
|
||||
|
||||
@ -131,7 +140,9 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
|
||||
不同于 C 语言和 Objective-C,Swift 中是可以对浮点数进行求余的。
|
||||
|
||||
8 % 2.5 // 等于 0.5
|
||||
```swift
|
||||
8 % 2.5 // 等于 0.5
|
||||
```
|
||||
|
||||
这个例子中,`8`除于`2.5`等于`3`余`0.5`,所以结果是一个`Double`值`0.5`。
|
||||
|
||||
@ -140,11 +151,11 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
### 自增和自减运算
|
||||
|
||||
和 C 语言一样,Swift 也提供了对变量本身加1或减1的自增(`++`)和自减(`--`)的缩略算符。其操作对象可以是整形和浮点型。
|
||||
|
||||
|
||||
var i = 0
|
||||
++i // 现在 i = 1
|
||||
|
||||
```swift
|
||||
var i = 0
|
||||
++i // 现在 i = 1
|
||||
```
|
||||
|
||||
每调用一次`++i`,`i`的值就会加1。实际上,`++i`是`i = i + 1`的简写,而`--i`是`i = i - 1`的简写。
|
||||
|
||||
@ -157,9 +168,11 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
|
||||
例如:
|
||||
|
||||
var a = 0
|
||||
let b = ++a // a 和 b 现在都是 1
|
||||
let c = a++ // a 现在 2, 但 c 是 a 自增前的值 1
|
||||
```swift
|
||||
var a = 0
|
||||
let b = ++a // a 和 b 现在都是 1
|
||||
let c = a++ // a 现在 2, 但 c 是 a 自增前的值 1
|
||||
```
|
||||
|
||||
上述例子,`let b = ++a`先把`a`加1了再返回`a`的值。所以`a`和`b`都是新值`1`。
|
||||
|
||||
@ -172,19 +185,22 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
|
||||
数值的正负号可以使用前缀`-`(即一元负号)来切换:
|
||||
|
||||
let three = 3
|
||||
let minusThree = -three // minusThree 等于 -3
|
||||
let plusThree = -minusThree // plusThree 等于 3, 或 "负负3"
|
||||
|
||||
```swift
|
||||
let three = 3
|
||||
let minusThree = -three // minusThree 等于 -3
|
||||
let plusThree = -minusThree // plusThree 等于 3, 或 "负负3"
|
||||
```
|
||||
|
||||
一元负号(`-`)写在操作数之前,中间没有空格。
|
||||
|
||||
### 一元正号运算符
|
||||
|
||||
一元正号(`+`)不做任何改变地返回操作数的值。
|
||||
一元正号(`+`)不做任何改变地返回操作数的值:
|
||||
|
||||
let minusSix = -6
|
||||
let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6
|
||||
```swift
|
||||
let minusSix = -6
|
||||
let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6
|
||||
```
|
||||
|
||||
虽然一元`+`什么都不会改变,但当你在使用一元负号来表达负数时,你可以使用一元正号来表达正数,如此你的代码会具有对称美。
|
||||
|
||||
@ -194,9 +210,10 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
|
||||
如同 C 语言,Swift 也提供把其他运算符和赋值运算(`=`)组合的复合赋值运算符,组合加运算(`+=`)是其中一个例子:
|
||||
|
||||
var a = 1
|
||||
a += 2 // a 现在是 3
|
||||
|
||||
```swift
|
||||
var a = 1
|
||||
a += 2 // a 现在是 3
|
||||
```
|
||||
|
||||
表达式`a += 2`是`a = a + 2`的简写,一个组合加运算就是把加法运算和赋值运算组合成进一个运算符里,同时完成两个运算任务。
|
||||
|
||||
@ -208,7 +225,7 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
<a name="comparison_operators"></a>
|
||||
## 比较运算符
|
||||
|
||||
所有标准 C 语言中的比较运算都可以在 Swift 中使用。
|
||||
所有标准 C 语言中的比较运算都可以在 Swift 中使用:
|
||||
|
||||
- 等于(`a == b`)
|
||||
- 不等于(`a != b`)
|
||||
@ -222,26 +239,26 @@ Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对
|
||||
|
||||
每个比较运算都返回了一个标识表达式是否成立的布尔值:
|
||||
|
||||
|
||||
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
|
||||
|
||||
```swift
|
||||
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" {
|
||||
print("hello, world")
|
||||
} else {
|
||||
print("I'm sorry \(name), but I don't recognize you")
|
||||
}
|
||||
// 输出 "hello, world", 因为 `name` 就是等于 "world"
|
||||
|
||||
```swift
|
||||
let name = "world"
|
||||
if name == "world" {
|
||||
print("hello, world")
|
||||
} else {
|
||||
print("I'm sorry \(name), but I don't recognize you")
|
||||
}
|
||||
// 输出 "hello, world", 因为 `name` 就是等于 "world"
|
||||
```
|
||||
|
||||
关于`if`语句,请看[控制流](./05_Control_Flow.html)。
|
||||
|
||||
@ -252,32 +269,36 @@ Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对
|
||||
|
||||
三目运算符是以下代码的缩写形式:
|
||||
|
||||
if question {
|
||||
answer1
|
||||
} else {
|
||||
answer2
|
||||
}
|
||||
```swift
|
||||
if question {
|
||||
answer1
|
||||
} else {
|
||||
answer2
|
||||
}
|
||||
```
|
||||
|
||||
这里有个计算表格行高的例子。如果有表头,那行高应比内容高度要高出50像素; 如果没有表头,只需高出20像素。
|
||||
这里有个计算表格行高的例子。如果有表头,那行高应比内容高度要高出50点;如果没有表头,只需高出20点:
|
||||
|
||||
let contentHeight = 40
|
||||
let hasHeader = true
|
||||
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
|
||||
// rowHeight 现在是 90
|
||||
```swift
|
||||
let contentHeight = 40
|
||||
let hasHeader = true
|
||||
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
|
||||
// rowHeight 现在是 90
|
||||
```
|
||||
|
||||
上面的写法比下面的代码更简洁:
|
||||
|
||||
这样写会比下面的代码简洁:
|
||||
|
||||
let contentHeight = 40
|
||||
let hasHeader = true
|
||||
var rowHeight = contentHeight
|
||||
if hasHeader {
|
||||
rowHeight = rowHeight + 50
|
||||
} else {
|
||||
rowHeight = rowHeight + 20
|
||||
}
|
||||
// rowHeight 现在是 90
|
||||
|
||||
```swift
|
||||
let contentHeight = 40
|
||||
let hasHeader = true
|
||||
var rowHeight = contentHeight
|
||||
if hasHeader {
|
||||
rowHeight = rowHeight + 50
|
||||
} else {
|
||||
rowHeight = rowHeight + 20
|
||||
}
|
||||
// rowHeight 现在是 90
|
||||
```
|
||||
|
||||
第一段代码例子使用了三目运算,所以一行代码就能让我们得到正确答案。这比第二段代码简洁得多,无需将`rowHeight`定义成变量,因为它的值无需在`if`语句中改变。
|
||||
|
||||
@ -293,7 +314,9 @@ Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对
|
||||
|
||||
空合并运算符是对以下代码的简短表达方法
|
||||
|
||||
a != nil ? a! : b
|
||||
```swift
|
||||
a != nil ? a! : b
|
||||
```
|
||||
|
||||
上述代码使用了三目运算符。当可选类型`a`的值不为空时,进行强制解封(`a!`)访问`a`中值,反之当`a`中值为空时,返回默认值b。无疑空合运算符(`??`)提供了一种更为优雅的方式去封装条件判断和解封两种行为,显得简洁以及更具可读性。
|
||||
|
||||
@ -302,21 +325,24 @@ Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对
|
||||
|
||||
下文例子采用空合并运算符,实现了在默认颜色名和可选自定义颜色名之间抉择:
|
||||
|
||||
```swift
|
||||
let defaultColorName = "red"
|
||||
var userDefinedColorName: String? //默认值为 nil
|
||||
|
||||
let defaultColorName = "red"
|
||||
var userDefinedColorName:String? //默认值为nil
|
||||
var colorNameToUse = userDefinedColorName ?? defaultColorName
|
||||
//userDefinedColorName的值为空 ,所以colorNameToUse的值为`red`
|
||||
|
||||
var colorNameToUse = userDefinedColorName ?? defaultColorName
|
||||
// userDefinedColorName 的值为空,所以 colorNameToUse 的值为 "red"
|
||||
```
|
||||
|
||||
`userDefinedColorName`变量被定义为一个可选`String`类型,默认值为`nil`。由于`userDefinedColorName`是一个可选类型,我们可以使用空合运算符去判断其值。在上一个例子中,通过空合运算符为一个名为`colorNameToUse`的变量赋予一个字符串类型初始值。
|
||||
由于`userDefinedColorName`值为空,因此表达式` userDefinedColorName ?? defaultColorName `返回`defaultColorName`的值,即`red`。
|
||||
由于`userDefinedColorName`值为空,因此表达式`userDefinedColorName ?? defaultColorName`返回`defaultColorName`的值,即`red`。
|
||||
|
||||
另一种情况,分配一个非空值(`non-nil`)给 `userDefinedColorName`,再次执行空合运算,运算结果为封包在`userDefaultColorName`中的值,而非默认值。
|
||||
另一种情况,分配一个非空值(`non-nil`)给`userDefinedColorName`,再次执行空合运算,运算结果为封包在`userDefaultColorName`中的值,而非默认值。
|
||||
|
||||
userDefinedColorName = "green"
|
||||
colorNameToUse = userDefinedColorName ?? defaultColorName
|
||||
//userDefinedColorName非空,因此colorNameToUsede的值为绿色
|
||||
```swift
|
||||
userDefinedColorName = "green"
|
||||
colorNameToUse = userDefinedColorName ?? defaultColorName
|
||||
// userDefinedColorName 非空,因此 colorNameToUse 的值为 "green"
|
||||
```
|
||||
|
||||
<a name="range_operators"></a>
|
||||
## 区间运算符
|
||||
@ -328,15 +354,16 @@ Swift 提供了两个方便表达一个区间的值的运算符。
|
||||
|
||||
闭区间运算符在迭代一个区间的所有值时是非常有用的,如在`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
|
||||
|
||||
```swift
|
||||
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
|
||||
```
|
||||
|
||||
关于`for-in`,请看[控制流](./05_Control_Flow.html)。
|
||||
|
||||
@ -345,19 +372,19 @@ Swift 提供了两个方便表达一个区间的值的运算符。
|
||||
半开区间(`a..<b`)定义一个从`a`到`b`但不包括`b`的区间。
|
||||
之所以称为半开区间,是因为该区间包含第一个值而不包括最后的值。
|
||||
|
||||
半开区间的实用性在于当你使用一个0始的列表(如数组)时,非常方便地从0数到列表的长度。
|
||||
|
||||
|
||||
let names = ["Anna", "Alex", "Brian", "Jack"]
|
||||
let count = names.count
|
||||
for i in 0..<count {
|
||||
print("第 \(i + 1) 个人叫 \(names[i])")
|
||||
}
|
||||
// 第 1 个人叫 Anna
|
||||
// 第 2 个人叫 Alex
|
||||
// 第 3 个人叫 Brian
|
||||
// 第 4 个人叫 Jack
|
||||
半开区间的实用性在于当你使用一个从0开始的列表(如数组)时,非常方便地从0数到列表的长度。
|
||||
|
||||
```swift
|
||||
let names = ["Anna", "Alex", "Brian", "Jack"]
|
||||
let count = names.count
|
||||
for i in 0..<count {
|
||||
print("第 \(i + 1) 个人叫 \(names[i])")
|
||||
}
|
||||
// 第 1 个人叫 Anna
|
||||
// 第 2 个人叫 Alex
|
||||
// 第 3 个人叫 Brian
|
||||
// 第 4 个人叫 Jack
|
||||
```
|
||||
|
||||
数组有4个元素,但`0..<count`只数到3(最后一个元素的下标),因为它是半开区间。关于数组,请查阅[数组](./04_Collection_Types.html#arrays)。
|
||||
|
||||
@ -376,14 +403,15 @@ Swift 提供了两个方便表达一个区间的值的运算符。
|
||||
|
||||
它是一个前置运算符,需紧跟在操作数之前,且不加空格。读作`非 a`,例子如下:
|
||||
|
||||
let allowedEntry = false
|
||||
if !allowedEntry {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "ACCESS DENIED"
|
||||
```swift
|
||||
let allowedEntry = false
|
||||
if !allowedEntry {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "ACCESS DENIED"
|
||||
```
|
||||
|
||||
|
||||
`if !allowedEntry`语句可以读作 "如果 非 alowed entry。",接下一行代码只有在如果 "非 allow entry" 为`true`,即`allowEntry`为`false`时被执行。
|
||||
`if !allowedEntry`语句可以读作“如果非 allowedEntry。”,接下一行代码只有在“非 allowedEntry”为`true`,即`allowEntry`为`false`时被执行。
|
||||
|
||||
在示例代码中,小心地选择布尔常量或变量有助于代码的可读性,并且避免使用双重逻辑非运算,或混乱的逻辑语句。
|
||||
|
||||
@ -391,51 +419,56 @@ Swift 提供了两个方便表达一个区间的值的运算符。
|
||||
|
||||
逻辑与(`a && b`)表达了只有`a`和`b`的值都为`true`时,整个表达式的值才会是`true`。
|
||||
|
||||
只要任意一个值为`false`,整个表达式的值就为`false`。事实上,如果第一个值为`false`,那么是不去计算第二个值的,因为它已经不可能影响整个表达式的结果了。这被称做 "短路计算(short-circuit evaluation)"。
|
||||
只要任意一个值为`false`,整个表达式的值就为`false`。事实上,如果第一个值为`false`,那么是不去计算第二个值的,因为它已经不可能影响整个表达式的结果了。这被称做“短路计算(short-circuit evaluation)”。
|
||||
|
||||
以下例子,只有两个`Bool`值都为`true`的时候才允许进入:
|
||||
|
||||
let enteredDoorCode = true
|
||||
let passedRetinaScan = false
|
||||
if enteredDoorCode && passedRetinaScan {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "ACCESS DENIED"
|
||||
|
||||
```swift
|
||||
let enteredDoorCode = true
|
||||
let passedRetinaScan = false
|
||||
if enteredDoorCode && passedRetinaScan {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "ACCESS DENIED"
|
||||
```
|
||||
|
||||
### 逻辑或
|
||||
|
||||
逻辑或(`a || b`)是一个由两个连续的`|`组成的中置运算符。它表示了两个逻辑表达式的其中一个为`true`,整个表达式就为`true`。
|
||||
|
||||
同逻辑与运算类似,逻辑或也是"短路计算"的,当左端的表达式为`true`时,将不计算右边的表达式了,因为它不可能改变整个表达式的值了。
|
||||
同逻辑与运算类似,逻辑或也是“短路计算”的,当左端的表达式为`true`时,将不计算右边的表达式了,因为它不可能改变整个表达式的值了。
|
||||
|
||||
以下示例代码中,第一个布尔值(`hasDoorKey`)为`false`,但第二个值(`knowsOverridePassword`)为`true`,所以整个表达是`true`,于是允许进入:
|
||||
|
||||
let hasDoorKey = false
|
||||
let knowsOverridePassword = true
|
||||
if hasDoorKey || knowsOverridePassword {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "Welcome!"
|
||||
```swift
|
||||
let hasDoorKey = false
|
||||
let knowsOverridePassword = true
|
||||
if hasDoorKey || knowsOverridePassword {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "Welcome!"
|
||||
```
|
||||
|
||||
### 逻辑运算符组合计算
|
||||
|
||||
我们可以组合多个逻辑运算来表达一个复合逻辑:
|
||||
|
||||
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "Welcome!"
|
||||
```swift
|
||||
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "Welcome!"
|
||||
```
|
||||
|
||||
这个例子使用了含多个`&&`和`||`的复合逻辑。但无论怎样,`&&`和`||`始终只能操作两个值。所以这实际是三个简单逻辑连续操作的结果。我们来解读一下:
|
||||
|
||||
如果我们输入了正确的密码并通过了视网膜扫描; 或者我们有一把有效的钥匙; 又或者我们知道紧急情况下重置的密码,我们就能把门打开进入。
|
||||
如果我们输入了正确的密码并通过了视网膜扫描,或者我们有一把有效的钥匙,又或者我们知道紧急情况下重置的密码,我们就能把门打开进入。
|
||||
|
||||
前两种情况,我们都不满足,所以前两个简单逻辑的结果是`false`,但是我们是知道紧急情况下重置的密码的,所以整个复杂表达式的值还是`true`。
|
||||
|
||||
@ -444,15 +477,15 @@ Swift 逻辑操作符`&&`和`||`是左结合的,这意味着拥有多元逻辑
|
||||
|
||||
### 使用括号来明确优先级
|
||||
|
||||
为了一个复杂表达式更容易读懂,在合适的地方使用括号来明确优先级是很有效的,虽然它并非必要的。在上个关于门的权限的例子中,我们给第一个部分加个括号,使用它看起来逻辑更明确:
|
||||
为了一个复杂表达式更容易读懂,在合适的地方使用括号来明确优先级是很有效的,虽然它并非必要的。在上个关于门的权限的例子中,我们给第一个部分加个括号,使它看起来逻辑更明确:
|
||||
|
||||
```swift
|
||||
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "Welcome!"
|
||||
```
|
||||
|
||||
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "Welcome!"
|
||||
|
||||
|
||||
这括号使得前两个值被看成整个逻辑表达中独立的一个部分。虽然有括号和没括号的输出结果是一样的,但对于读代码的人来说有括号的代码更清晰。可读性比简洁性更重要,请在可以让你代码变清晰地地方加个括号吧!
|
||||
这括号使得前两个值被看成整个逻辑表达中独立的一个部分。虽然有括号和没括号的输出结果是一样的,但对于读代码的人来说有括号的代码更清晰。可读性比简洁性更重要,请在可以让你代码变清晰的地方加个括号吧!
|
||||
|
||||
@ -42,7 +42,7 @@ Swift 的`String`和`Character`类型提供了一个快速的,兼容 Unicode
|
||||
<a name="string_literals"></a>
|
||||
## 字符串字面量(String Literals)
|
||||
|
||||
您可以在您的代码中包含一段预定义的字符串值作为字符串字面量。字符串字面量是由双引号 ("") 包裹着的具有固定顺序的文本字符集。
|
||||
您可以在您的代码中包含一段预定义的字符串值作为字符串字面量。字符串字面量是由双引号 (`""`) 包裹着的具有固定顺序的文本字符集。
|
||||
字符串字面量可以用于为常量和变量提供初始值:
|
||||
|
||||
```swift
|
||||
@ -50,8 +50,9 @@ let someString = "Some string literal value"
|
||||
```
|
||||
|
||||
注意`someString`常量通过字符串字面量进行初始化,Swift 会推断该常量为`String`类型。
|
||||
> 注意:
|
||||
> 更多关于在字面量的特殊字符,请查看 [Special Characters in String Literals](#special_characters_in_string_literals) 。
|
||||
|
||||
> 注意:
|
||||
更多关于在字符串字面量中使用特殊字符的信息,请查看 [字符串字面量的特殊字符](#special_characters_in_string_literals) 。
|
||||
|
||||
|
||||
<a name="initializing_an_empty_string"></a>
|
||||
@ -83,13 +84,14 @@ if emptyString.isEmpty {
|
||||
var variableString = "Horse"
|
||||
variableString += " and carriage"
|
||||
// variableString 现在为 "Horse and carriage"
|
||||
|
||||
let constantString = "Highlander"
|
||||
constantString += " and another Highlander"
|
||||
// 这会报告一个编译错误 (compile-time error) - 常量不可以被修改。
|
||||
// 这会报告一个编译错误 (compile-time error) - 常量字符串不可以被修改。
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 在 Objective-C 和 Cocoa 中,您需要通过选择两个不同的类(`NSString`和`NSMutableString`)来指定该字符串是否可以被修改。
|
||||
> 注意:
|
||||
在 Objective-C 和 Cocoa 中,您需要通过选择两个不同的类(`NSString`和`NSMutableString`)来指定字符串是否可以被修改。
|
||||
|
||||
<a name="strings_are_value_types"></a>
|
||||
## 字符串是值类型(Strings Are Value Types)
|
||||
@ -100,11 +102,11 @@ Swift 的`String`类型是值类型。
|
||||
值类型在 [结构体和枚举是值类型](./09_Classes_and_Structures.html#structures_and_enumerations_are_value_types) 中进行了详细描述。
|
||||
|
||||
> 注意:
|
||||
> 与 Cocoa 中的`NSString`不同,当您在 Cocoa 中创建了一个`NSString`实例,并将其传递给一个函数/方法,或者赋值给一个变量,您传递或赋值的是该`NSString`实例的一个引用,除非您特别要求进行值拷贝,否则字符串不会生成新的副本来进行赋值操作。
|
||||
与 Cocoa 中的`NSString`不同,当您在 Cocoa 中创建了一个`NSString`实例,并将其传递给一个函数/方法,或者赋值给一个变量,您传递或赋值的是该`NSString`实例的一个引用,除非您特别要求进行值拷贝,否则字符串不会生成新的副本来进行赋值操作。
|
||||
|
||||
Swift 默认字符串拷贝的方式保证了在函数/方法中传递的是字符串的值。
|
||||
很明显无论该值来自于哪里,都是您独自拥有的。
|
||||
您可以放心您传递的字符串本身不会被更改。
|
||||
您可以确信传递的字符串不会被修改,除非你自己去修改它。
|
||||
|
||||
在实际编译时,Swift 编译器会优化字符串的使用,使实际的复制只发生在绝对必要的情况下,这意味着您将字符串作为值类型的同时可以获得极高的性能。
|
||||
|
||||
@ -124,7 +126,7 @@ for character in "Dog!🐶".characters {
|
||||
// 🐶
|
||||
```
|
||||
|
||||
for-in 循环在 [For Loops](./05_Control_Flow.html#for_loops) 中进行了详细描述。
|
||||
`for-in`循环在 [For Loops](./05_Control_Flow.html#for_loops) 中进行了详细描述。
|
||||
|
||||
另外,通过标明一个`Character`类型并用字符字面量进行赋值,可以建立一个独立的字符常量或变量:
|
||||
|
||||
@ -148,7 +150,7 @@ print(catString)
|
||||
```swift
|
||||
let string1 = "hello"
|
||||
let string2 = " there"
|
||||
var welcome = string1 + string2
|
||||
var welcome = string1 + string2
|
||||
// welcome 现在等于 "hello there"
|
||||
```
|
||||
|
||||
@ -157,10 +159,10 @@ var welcome = string1 + string2
|
||||
```swift
|
||||
var instruction = "look over"
|
||||
instruction += string2
|
||||
// instruction 现在等于 "look over there"
|
||||
// instruction 现在等于 "look over there"
|
||||
```
|
||||
|
||||
您可以用`append`方法将一个字符附加到一个字符串变量的尾部:
|
||||
您可以用`append()`方法将一个字符附加到一个字符串变量的尾部:
|
||||
|
||||
```swift
|
||||
let exclamationMark: Character = "!"
|
||||
@ -169,7 +171,7 @@ welcome.append(exclamationMark)
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 您不能将一个字符串或者字符添加到一个已经存在的字符变量上,因为字符变量只能包含一个字符。
|
||||
您不能将一个字符串或者字符添加到一个已经存在的字符变量上,因为字符变量只能包含一个字符。
|
||||
|
||||
|
||||
<a name="string_interpolation"></a>
|
||||
@ -181,14 +183,14 @@ welcome.append(exclamationMark)
|
||||
```swift
|
||||
let multiplier = 3
|
||||
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
|
||||
// message 是 "3 times 2.5 is 7.5"
|
||||
// message is "3 times 2.5 is 7.5"
|
||||
```
|
||||
|
||||
在上面的例子中,`multiplier`作为`\(multiplier)`被插入到一个字符串字面量中。
|
||||
在上面的例子中,`multiplier`作为`\(multiplier)`被插入到一个字符串常量量中。
|
||||
当创建字符串执行插值计算时此占位符会被替换为`multiplier`实际的值。
|
||||
|
||||
`multiplier`的值也作为字符串中后面表达式的一部分。
|
||||
该表达式计算`Double(multiplier) * 2.5`的值并将结果 (7.5) 插入到字符串中。
|
||||
该表达式计算`Double(multiplier) * 2.5`的值并将结果 (`7.5`) 插入到字符串中。
|
||||
在这个例子中,表达式写为`\(Double(multiplier) * 2.5)`并包含在字符串字面量中。
|
||||
|
||||
> 注意:
|
||||
@ -200,17 +202,18 @@ let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
|
||||
|
||||
Unicode 是一个国际标准,用于文本的编码和表示。
|
||||
它使您可以用标准格式表示来自任意语言几乎所有的字符,并能够对文本文件或网页这样的外部资源中的字符进行读写操作。
|
||||
Swift 的字符串和字符类型是完全兼容 Unicode 标准的。
|
||||
Swift 的`String`和`Character`类型是完全兼容 Unicode 标准的。
|
||||
|
||||
<a name="unicode_scalars"></a>
|
||||
### Unicode 标量(Unicode Scalars)
|
||||
|
||||
Swift 的`String`类型是基于 *Unicode 标量* 建立的。
|
||||
Unicode 标量是对应字符的唯一21位数字或者修饰符,例如`U+0061`表示小写的拉丁字母(`LATIN SMALL LETTER A`)("`a`"),`U+1F425`表示小鸡表情(`FRONT-FACING BABY CHICK`) ("`🐥`")
|
||||
Unicode 标量是对应字符或者修饰符的唯一的21位数字,例如`U+0061`表示小写的拉丁字母(`LATIN SMALL LETTER A`)("`a`"),`U+1F425`表示小鸡表情(`FRONT-FACING BABY CHICK`) ("`🐥`")。
|
||||
|
||||
> 注意:
|
||||
> Unicode *码位(code poing)* 的范围是`U+0000`到`U+D7FF`或者`U+E000`到`U+10FFFF`。Unicode 标量不包括 Unicode *代理项(surrogate pair)* 码位,其码位范围是`U+D800`到`U+DFFF`。
|
||||
|
||||
注意不是所有的21位 Unicode 标量都代表一个字符,因为有一些标量是保留给未来分配的。已经代表一个典型字符的标量都有自己的名字,例如上面例子中的`LATIN SMALL LETTER A`和`FRONT-FACING BABY CHICK`。
|
||||
注意不是所有的21位 Unicode 标量都代表一个字符,因为有一些标量是留作未来分配的。已经代表一个典型字符的标量都有自己的名字,例如上面例子中的`LATIN SMALL LETTER A`和`FRONT-FACING BABY CHICK`。
|
||||
|
||||
<a name="special_characters_in_string_literals"></a>
|
||||
### 字符串字面量的特殊字符 (Special Characters in String Literals)
|
||||
@ -221,7 +224,7 @@ Unicode 标量是对应字符的唯一21位数字或者修饰符,例如`U+0061
|
||||
* Unicode 标量,写成`\u{n}`(u为小写),其中`n`为任意一到八位十六进制数且可用的 Unicode 位码。
|
||||
|
||||
下面的代码为各种特殊字符的使用示例。
|
||||
`wiseWords`常量包含了两个双引号;
|
||||
`wiseWords`常量包含了两个双引号。
|
||||
`dollarSign`、`blackHeart`和`sparklingHeart`常量演示了三种不同格式的 Unicode 标量:
|
||||
|
||||
```swift
|
||||
@ -234,11 +237,13 @@ let sparklingHeart = "\u{1F496}" // 💖, Unicode 标量 U+1F496
|
||||
|
||||
<a name="extended_grapheme_clusters"></a>
|
||||
### 可扩展的字形群集(Extended Grapheme Clusters)
|
||||
|
||||
每一个 Swift 的`Character`类型代表一个可扩展的字形群。
|
||||
一个可扩展的字形群是一个或者更多可生成人类可读的字符 Unicode 标量的有序排列。
|
||||
举个例子,字母 é 可以用单一的 Unicode 标量 é (`LATIN SMALL LETTER E WITH ACUTE`, 或者`U+00E9`)来表示。然而一个标准的字母 e (`LATIN SMALL LETTER E`或者`U+0065`) 加上一个急促重音(`COMBINING ACTUE ACCENT`)的标量(`U+0301`),这样一对标量就表示了同样的字母 é。
|
||||
这个急促重音的标量形象的将 e 转换成了 é。
|
||||
在这两种情况中,字母 é 代表了一个单一的 Swift 的字符串,同时代表了一个可扩展的字形群。
|
||||
一个可扩展的字形群是一个或多个可生成人类可读的字符 Unicode 标量的有序排列。
|
||||
举个例子,字母`é`可以用单一的 Unicode 标量`é`(`LATIN SMALL LETTER E WITH ACUTE`, 或者`U+00E9`)来表示。然而一个标准的字母`e`(`LATIN SMALL LETTER E`或者`U+0065`) 加上一个急促重音(`COMBINING ACTUE ACCENT`)的标量(`U+0301`),这样一对标量就表示了同样的字母`é`。
|
||||
这个急促重音的标量形象的将`e`转换成了`é`。
|
||||
|
||||
在这两种情况中,字母`é`代表了一个单一的 Swift 的`Character`值,同时代表了一个可扩展的字形群。
|
||||
在第一种情况,这个字形群包含一个单一标量;而在第二种情况,它是包含两个标量的字形群:
|
||||
|
||||
```swift
|
||||
@ -247,9 +252,9 @@ let combinedEAcute: Character = "\u{65}\u{301}" // e 后面加上 ́
|
||||
// eAcute 是 é, combinedEAcute 是 é
|
||||
```
|
||||
|
||||
可扩展的字符群集是一个灵活的方法,用许多复杂的脚本字符表示单一字符。
|
||||
可扩展的字符群集是一个灵活的方法,用许多复杂的脚本字符表示单一的`Character`值。
|
||||
例如,来自朝鲜语字母表的韩语音节能表示为组合或分解的有序排列。
|
||||
在 Swift 都会表示为同一个单一的字符:
|
||||
在 Swift 都会表示为同一个单一的`Character`值:
|
||||
|
||||
|
||||
```swift
|
||||
@ -258,14 +263,14 @@ let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ, ᅡ, ᆫ
|
||||
// precomposed 是 한, decomposed 是 한
|
||||
```
|
||||
|
||||
可拓展的字符群集可以使包围记号(例如`COMBINING ENCLOSING CIRCLE`或者`U+20DD`)的标量包围其他 Unicode 标量,作为一个单一的字符:
|
||||
可拓展的字符群集可以使包围记号(例如`COMBINING ENCLOSING CIRCLE`或者`U+20DD`)的标量包围其他 Unicode 标量,作为一个单一的`Character`值:
|
||||
|
||||
```swift
|
||||
let enclosedEAcute: Character = "\u{E9}\u{20DD}"
|
||||
// enclosedEAcute 是 é⃝
|
||||
```
|
||||
|
||||
局部的指示符号的 Unicode 标量可以组合成一个单一的字符,例如 `REGIONAL INDICATOR SYMBOL LETTER U`(`U+1F1FA`)和`REGIONAL INDICATOR SYMBOL LETTER S`(`U+1F1F8`):
|
||||
局部的指示符号的 Unicode 标量可以组合成一个单一的`Character`值,例如`REGIONAL INDICATOR SYMBOL LETTER U`(`U+1F1FA`)和`REGIONAL INDICATOR SYMBOL LETTER S`(`U+1F1F8`):
|
||||
|
||||
|
||||
```swift
|
||||
@ -276,7 +281,7 @@ let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
|
||||
<a name="counting_characters"></a>
|
||||
## 计算字符数量 (Counting Characters)
|
||||
|
||||
如果想要获得一个字符串中字符的数量,可以使用字符串的characters属性的count属性:
|
||||
如果想要获得一个字符串中`Character`值的数量,可以使用字符串的`characters`属性的`count`属性:
|
||||
|
||||
```swift
|
||||
let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪"
|
||||
@ -284,9 +289,9 @@ print("unusualMenagerie has \(unusualMenagerie.characters.count) characters")
|
||||
// 打印输出 "unusualMenagerie has 40 characters"
|
||||
```
|
||||
|
||||
注意在 Swift 中,使用可拓展的字符群集作为字符来连接或改变字符串时,并不一定会更改字符串的字符数量。
|
||||
注意在 Swift 中,使用可拓展的字符群集作为`Character`值来连接或改变字符串时,并不一定会更改字符串的字符数量。
|
||||
|
||||
例如,如果你用四个字符的单词 cafe 初始化一个新的字符串,然后添加一个 `COMBINING ACTUE ACCENT`(`U+0301`)作为字符串的结尾。最终这个字符串的字符数量仍然是4,因为第四个字符是 é ,而不是 e :
|
||||
例如,如果你用四个字符的单词`cafe`初始化一个新的字符串,然后添加一个`COMBINING ACTUE ACCENT`(`U+0301`)作为字符串的结尾。最终这个字符串的字符数量仍然是`4`,因为第四个字符是`é`,而不是`e`:
|
||||
|
||||
```swift
|
||||
var word = "cafe"
|
||||
@ -300,67 +305,61 @@ print("the number of characters in \(word) is \(word.characters.count)")
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 可扩展的字符群集可以组成一个或者多个 Unicode 标量。这意味着不同的字符以及相同字符的不同表示方式可能需要不同数量的内存空间来存储。所以 Swift 中的字符在一个字符串中并不一定占用相同的内存空间数量。因此在没有获取字符串的可扩展的字符群的范围时候,就不能计算出字符串的字符数量。如果您正在处理一个长字符串,需要注意`count(_:)`函数必须遍历全部的 Unicode 标量,来确定字符串的字符数量。
|
||||
> 可扩展的字符群集可以组成一个或者多个 Unicode 标量。这意味着不同的字符以及相同字符的不同表示方式可能需要不同数量的内存空间来存储。所以 Swift 中的字符在一个字符串中并不一定占用相同的内存空间数量。因此在没有获取字符串的可扩展的字符群的范围时候,就不能计算出字符串的字符数量。如果您正在处理一个长字符串,需要注意`characters`属性必须遍历全部的 Unicode 标量,来确定字符串的字符数量。
|
||||
>
|
||||
> 另外需要注意的是通过`count(_:)`返回的字符数量并不总是与包含相同字符的`NSString`的`length`属性相同。`NSString`的`length`属性是利用 UTF-16 表示的十六位代码单元数字,而不是 Unicode 可扩展的字符群集。作为佐证,当一个`NSString`的`length`属性被一个Swift的`String`值访问时,实际上是调用了`utf16Count`。
|
||||
> 另外需要注意的是通过`characters`属性返回的字符数量并不总是与包含相同字符的`NSString`的`length`属性相同。`NSString`的`length`属性是利用 UTF-16 表示的十六位代码单元数字,而不是 Unicode 可扩展的字符群集。作为佐证,当一个`NSString`的`length`属性被一个Swift的`String`值访问时,实际上是调用了`utf16Count`。
|
||||
|
||||
|
||||
<a name="accessing_and_modifying_a_string"></a>
|
||||
## 访问和修改字符串 (Accessing and Modifying a String)
|
||||
你可以通字符串的属性和方法来访问和读取一个它,当然也可以用下标语法完成。
|
||||
|
||||
你可以通字符串的属性和方法来访问和读取它,当然也可以用下标语法完成。
|
||||
|
||||
<a name="string_indices"></a>
|
||||
### 字符串索引 (String Indices)
|
||||
每一个字符串都有一个关联的索引(*index*)类型,`String.index`,它对应着字符串中的每一个字符的位置。
|
||||
|
||||
前面提到,不同的字符可能会占用不同的内存空间数量,所以要知道字符的确定位置,就必须从字符串开头遍历每一个 Unicode 标量到字符串结尾。因此,Swift 的字符串不能用整数(integer)做索引。
|
||||
每一个`String`值都有一个关联的索引(*index*)类型,`String.Index`,它对应着字符串中的每一个`Character`的位置。
|
||||
|
||||
使用`startIndex`属性可以获取字符串的第一个字符。使用`endIndex`属性可以获取最后的位置(译者注:其实endIndex在值上与字符串的长度相等)。如果字符串是空值,`startIndex`和`endIndex`是相等的。
|
||||
前面提到,不同的字符可能会占用不同数量的内存空间,所以要知道`Character`的确定位置,就必须从`String`开头遍历每一个 Unicode 标量直到结尾。因此,Swift 的字符串不能用整数(integer)做索引。
|
||||
|
||||
使用`startIndex`属性可以获取一个`String`的第一个`Character`的索引。使用`endIndex`属性可以获取最后一个`Character`的后一个位置的索引。因此,`endIndex`属性不能作为一个字符串的有效下标。如果`String`是空串,`startIndex`和`endIndex`是相等的。
|
||||
|
||||
通过调用`String.Index`的`predecessor()`方法,可以立即得到前面一个索引,调用`successor()`方法可以立即得到后面一个索引。任何一个`String`的索引都可以通过锁链作用的这些方法来获取另一个索引,也可以调用`advancedBy(_:)`方法来获取。但如果尝试获取出界的字符串索引,就会抛出一个运行时错误。
|
||||
|
||||
你可以使用下标语法来访问`String`特定索引的`Character`。
|
||||
|
||||
```swift
|
||||
let greeting = "Guten Tag"
|
||||
println(greeting.startIndex)
|
||||
// 0
|
||||
println(greeting.endIndex)
|
||||
// 9
|
||||
```
|
||||
|
||||
你可以通过下表来获得`String`对应位置的`Character`:
|
||||
|
||||
```swift
|
||||
greeting[greeting.startIndex]
|
||||
// G
|
||||
```
|
||||
|
||||
通过调用`String.Index`的`predecessor()`方法,可以立即得到前面一个索引,调用`successor()`方法可以立即得到后面一个索引。任何一个字符串的索引都可以通过锁链作用的这些方法来获取另一个索引,也可以调用`advance(start:n:)`函数来获取。但如果尝试获取出界的字符串索引,就会抛出一个运行时错误。
|
||||
你可以使用下标语法来访问字符在字符串的确切索引。尝试获取出界的字符串索引,仍然抛出一个运行时错误。
|
||||
|
||||
```swift
|
||||
let greeting = "Guten Tag"
|
||||
let greeting = "Guten Tag!"
|
||||
greeting[greeting.startIndex]
|
||||
// G
|
||||
greeting[greeting.endIndex.predecessor()]
|
||||
// g
|
||||
// !
|
||||
greeting[greeting.startIndex.successor()]
|
||||
// u
|
||||
let index = advance(greeting.startIndex, 7)
|
||||
let index = greeting.startIndex.advancedBy(7)
|
||||
greeting[index]
|
||||
// a
|
||||
greeting[greeting.endIndex] // 错误
|
||||
greeting.endIndex.successor() // 错误
|
||||
```
|
||||
|
||||
使用全局函数`indices`会创建一个包含全部索引的范围(`Range`),用来在一个字符串中访问分立的字符。
|
||||
试图获取越界索引对应的`Character`,将引发一个运行时错误。
|
||||
|
||||
```swift
|
||||
greeting[greeting.endIndex] // error
|
||||
greeting.endIndex.successor() // error
|
||||
```
|
||||
|
||||
使用`characters`属性的`indices`属性会创建一个包含全部索引的范围(`Range`),用来在一个字符串中访问单个字符。
|
||||
|
||||
```swift
|
||||
for index in greeting.characters.indices {
|
||||
print("\(greeting[index]) ", appendNewline: false)
|
||||
print("\(greeting[index]) ", terminator: " ")
|
||||
}
|
||||
// prints "G u t e n T a g !"
|
||||
// 打印输出 "G u t e n T a g !"
|
||||
```
|
||||
|
||||
<a name="inserting_and_removing"></a>
|
||||
### 插入和删除 (Inserting and Removing)
|
||||
|
||||
调用`insert(_:atIndex:)`方法可以在一个字符串的指定索引插入一个字符。
|
||||
|
||||
```swift
|
||||
@ -369,10 +368,10 @@ welcome.insert("!", atIndex: welcome.endIndex)
|
||||
// welcome now 现在等于 "hello!"
|
||||
```
|
||||
|
||||
调用`splice(_:atIndex:)`方法可以在一个字符串的指定索引插入一个字符串。
|
||||
调用`insertContentsOf(_:at:)`方法可以在一个字符串的指定索引插入一个字符串。
|
||||
|
||||
```swift
|
||||
welcome.splice(" there".characters, atIndex: welcome.endIndex.predecessor())
|
||||
welcome.insertContentsOf(" there".characters, at: welcome.endIndex.predecessor())
|
||||
// welcome 现在等于 "hello there!"
|
||||
```
|
||||
|
||||
@ -381,13 +380,12 @@ welcome.splice(" there".characters, atIndex: welcome.endIndex.predecessor())
|
||||
```swift
|
||||
welcome.removeAtIndex(welcome.endIndex.predecessor())
|
||||
// welcome 现在等于 "hello there"
|
||||
// 翻译的人解释:最后还有一个换行符,所以这里删除的是 !
|
||||
```
|
||||
|
||||
调用`removeRange(_:)`方法可以在一个字符串的指定索引删除一个子字符串。
|
||||
|
||||
```swift
|
||||
let range = advance(welcome.endIndex, -6)..<welcome.endIndex
|
||||
let range = welcome.endIndex.advancedBy(-6)..<welcome.endIndex
|
||||
welcome.removeRange(range)
|
||||
// welcome 现在等于 "hello"
|
||||
```
|
||||
@ -400,6 +398,7 @@ Swift 提供了三种方式来比较文本值:字符串字符相等、前缀
|
||||
|
||||
<a name="string_and_character_equality"></a>
|
||||
### 字符串/字符相等 (String and Character Equality)
|
||||
|
||||
字符串/字符可以用等于操作符(`==`)和不等于操作符(`!=`),详细描述在[比较运算符](./02_Basic_Operators.html#comparison_operators):
|
||||
|
||||
```swift
|
||||
@ -413,13 +412,15 @@ if quotation == sameQuotation {
|
||||
|
||||
如果两个字符串(或者两个字符)的可扩展的字形群集是标准相等的,那就认为它们是相等的。在这个情况下,即使可扩展的字形群集是有不同的 Unicode 标量构成的,只要它们有同样的语言意义和外观,就认为它们标准相等。
|
||||
|
||||
例如,`LATIN SMALL LETTER E WITH ACUTE`(`U+00E9`)就是标准相等于`LATIN SMALL LETTER E`(`U+0065`)后面加上`COMBINING ACUTE ACCENT`(`U+0301`)。这两个字符群集都有效的表示字符 é ,所以它们被认为是标准相等的:
|
||||
例如,`LATIN SMALL LETTER E WITH ACUTE`(`U+00E9`)就是标准相等于`LATIN SMALL LETTER E`(`U+0065`)后面加上`COMBINING ACUTE ACCENT`(`U+0301`)。这两个字符群集都是表示字符`é`的有效方式,所以它们被认为是标准相等的:
|
||||
|
||||
```swift
|
||||
// "Voulez-vous un café?" 使用 LATIN SMALL LETTER E WITH ACUTE
|
||||
let eAcuteQuestion = "Voulez-vous un caf\u{E9}?"
|
||||
|
||||
// "Voulez-vous un café?" 使用 LATIN SMALL LETTER E and COMBINING ACUTE ACCENT
|
||||
let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?"
|
||||
|
||||
if eAcuteQuestion == combinedEAcuteQuestion {
|
||||
print("These two strings are considered equal")
|
||||
}
|
||||
@ -430,7 +431,9 @@ if eAcuteQuestion == combinedEAcuteQuestion {
|
||||
|
||||
```swift
|
||||
let latinCapitalLetterA: Character = "\u{41}"
|
||||
|
||||
let cyrillicCapitalLetterA: Character = "\u{0410}"
|
||||
|
||||
if latinCapitalLetterA != cyrillicCapitalLetterA {
|
||||
print("These two characters are not equivalent")
|
||||
}
|
||||
@ -444,7 +447,8 @@ if latinCapitalLetterA != cyrillicCapitalLetterA {
|
||||
<a name="prefix_and_suffix_equality"></a>
|
||||
### 前缀/后缀相等 (Prefix and Suffix Equality)
|
||||
|
||||
通过调用字符串的`hasPrefix(_:)`/`hasSuffix(_:)`方法来检查字符串是否拥有特定前缀/后缀,两个方法均需要以字符串作为参数传入并传出`Boolean`值。
|
||||
通过调用字符串的`hasPrefix(_:)`/`hasSuffix(_:)`方法来检查字符串是否拥有特定前缀/后缀,两个方法均接收一个`String`类型的参数,并返回一个布尔值。
|
||||
|
||||
下面的例子以一个字符串数组表示莎士比亚话剧《罗密欧与朱丽叶》中前两场的场景位置:
|
||||
|
||||
```swift
|
||||
@ -493,15 +497,16 @@ print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> `hasPrefix(_:)`和`hasSuffix(_:)`方法都是在每个字符串中一个一个字符的比较其可扩展的字符群集是否标准相等,详细描述在[字符串/字符相等](#string_and_character_equality)。
|
||||
> `hasPrefix(_:)`和`hasSuffix(_:)`方法都是在每个字符串中逐字符比较其可扩展的字符群集是否标准相等,详细描述在[字符串/字符相等](#string_and_character_equality)。
|
||||
|
||||
|
||||
<a name="unicode_representations_of_strings"></a>
|
||||
## 字符串的 Unicode 表示形式(Unicode Representations of Strings)
|
||||
|
||||
当一个 Unicode 字符串被写进文本文件或者其他储存时,字符串中的 Unicode 标量会用 Unicode 定义的几种编码格式编码。每一个字符串中的小块编码都被称为代码单元。这些包括 UTF-8 编码格式(编码字符串为8位的代码单元), UTF-16 编码格式(编码字符串位16位的代码单元),以及 UTF-32 编码格式(编码字符串32位的代码单元)。
|
||||
|
||||
Swift 提供了几种不同的方式来访问字符串的 Unicode 表示形式。
|
||||
您可以利用`for-in`来对字符串进行遍历,从而以 Unicode 可扩展的字符群集的方式访问每一个字符值。
|
||||
您可以利用`for-in`来对字符串进行遍历,从而以 Unicode 可扩展的字符群集的方式访问每一个`Character`值。
|
||||
该过程在 [使用字符](#working_with_characters) 中进行了描述。
|
||||
|
||||
另外,能够以其他三种 Unicode 兼容的方式访问字符串的值:
|
||||
@ -519,7 +524,8 @@ let dogString = "Dog‼🐶"
|
||||
|
||||
<a name="UTF-8_representation"></a>
|
||||
### UTF-8 表示
|
||||
您可以通过遍历字符串的`utf8`属性来访问它的`UTF-8`表示。
|
||||
|
||||
您可以通过遍历`String`的`utf8`属性来访问它的`UTF-8`表示。
|
||||
其为`String.UTF8View`类型的属性,`UTF8View`是无符号8位 (`UInt8`) 值的集合,每一个`UInt8`值都是一个字符的 UTF-8 表示:
|
||||
|
||||
<table style='text-align:center'>
|
||||
@ -562,21 +568,21 @@ let dogString = "Dog‼🐶"
|
||||
|
||||
```swift
|
||||
for codeUnit in dogString.utf8 {
|
||||
print("\(codeUnit) ", appendNewline: false)
|
||||
print("\(codeUnit) ", terminator: "")
|
||||
}
|
||||
print("")
|
||||
// 68 111 103 226 128 188 240 159 144 182
|
||||
```
|
||||
|
||||
上面的例子中,前三个10进制代码单元值 (68, 111, 103) 代表了字符`D`、`o`和 `g`,它们的 UTF-8 表示与 ASCII 表示相同。
|
||||
接下来的三个10进制代码单元值 (226, 128, 188) 是`DOUBLE EXCLAMATION MARK`的3字节 UTF-8 表示。
|
||||
最后的四个代码单元值 (240, 159, 144, 182) 是`DOG FACE`的4字节 UTF-8 表示。
|
||||
上面的例子中,前三个10进制`codeUnit`值 (`68`, `111`, `103`) 代表了字符`D`、`o`和 `g`,它们的 UTF-8 表示与 ASCII 表示相同。
|
||||
接下来的三个10进制`codeUnit`值 (`226`, `128`, `188`) 是`DOUBLE EXCLAMATION MARK`的3字节 UTF-8 表示。
|
||||
最后的四个`codeUnit`值 (`240`, `159`, `144`, `182`) 是`DOG FACE`的4字节 UTF-8 表示。
|
||||
|
||||
|
||||
<a name="UTF-16_representation"></a>
|
||||
### UTF-16 表示
|
||||
|
||||
您可以通过遍历字符串的`utf16`属性来访问它的`UTF-16`表示。
|
||||
您可以通过遍历`String`的`utf16`属性来访问它的`UTF-16`表示。
|
||||
其为`String.UTF16View`类型的属性,`UTF16View`是无符号16位 (`UInt16`) 值的集合,每一个`UInt16`都是一个字符的 UTF-16 表示:
|
||||
|
||||
<table style='text-align:center'>
|
||||
@ -611,27 +617,27 @@ print("")
|
||||
|
||||
```swift
|
||||
for codeUnit in dogString.utf16 {
|
||||
print("\(codeUnit) ")
|
||||
print("\(codeUnit) ", terminator: "")
|
||||
}
|
||||
print("\n")
|
||||
print("")
|
||||
// 68 111 103 8252 55357 56374
|
||||
```
|
||||
|
||||
同样,前三个代码单元值 (68, 111, 103) 代表了字符`D`、`o`和`g`,它们的 UTF-16 代码单元和 UTF-8 完全相同(因为这些 Unicode 标量表示 ASCII 字符)。
|
||||
同样,前三个`codeUnit`值 (`68`, `111`, `103`) 代表了字符`D`、`o`和`g`,它们的 UTF-16 代码单元和 UTF-8 完全相同(因为这些 Unicode 标量表示 ASCII 字符)。
|
||||
|
||||
第四个代码单元值 (8252) 是一个等于十六进制203C的的十进制值。这个代表了`DOUBLE EXCLAMATION MARK`字符的 Unicode 标量值`U+203C`。这个字符在 UTF-16 中可以用一个代码单元表示。
|
||||
第四个`codeUnit`值 (`8252`) 是一个等于十六进制`203C`的的十进制值。这个代表了`DOUBLE EXCLAMATION MARK`字符的 Unicode 标量值`U+203C`。这个字符在 UTF-16 中可以用一个代码单元表示。
|
||||
|
||||
第五和第六个代码单元值 (55357 和 56374) 是`DOG FACE`字符的UTF-16 表示。
|
||||
第一个值为`U+D83D`(十进制值为 55357),第二个值为`U+DC36`(十进制值为 56374)。
|
||||
第五和第六个`codeUnit`值 (`55357`和`56374`) 是`DOG FACE`字符的 UTF-16 表示。
|
||||
第一个值为`U+D83D`(十进制值为`55357`),第二个值为`U+DC36`(十进制值为`56374`)。
|
||||
|
||||
<a name="unicode_scalars_representation"></a>
|
||||
### Unicode 标量表示 (Unicode Scalars Representation)
|
||||
|
||||
您可以通过遍历字符串的`unicodeScalars`属性来访问它的 Unicode 标量表示。
|
||||
其为`UnicodeScalarView`类型的属性, `UnicodeScalarView`是`UnicodeScalar`的集合。
|
||||
您可以通过遍历`String`值的`unicodeScalars`属性来访问它的 Unicode 标量表示。
|
||||
其为`UnicodeScalarView`类型的属性,`UnicodeScalarView`是`UnicodeScalar`的集合。
|
||||
`UnicodeScalar`是21位的 Unicode 代码点。
|
||||
|
||||
每一个`UnicodeScalar`拥有一个值属性,可以返回对应的21位数值,用`UInt32`来表示:
|
||||
每一个`UnicodeScalar`拥有一个`value`属性,可以返回对应的21位数值,用`UInt32`来表示:
|
||||
|
||||
|
||||
<table style='text-align:center'>
|
||||
@ -664,18 +670,18 @@ print("\n")
|
||||
|
||||
```swift
|
||||
for scalar in dogString.unicodeScalars {
|
||||
print("\(scalar.value) ")
|
||||
print("\(scalar.value) ", terminator: "")
|
||||
}
|
||||
print("\n")
|
||||
print("")
|
||||
// 68 111 103 8252 128054
|
||||
```
|
||||
|
||||
前三个代码单元值 (68, 111, 103) 仍然代表字符`D`、`o`和`g`。
|
||||
第四个代码单元值 (8252) 仍然是一个等于十六进制203C的的十进制值。这个代表了`DOUBLE EXCLAMATION MARK`字符的 Unicode 标量`U+203C`。
|
||||
前三个`UnicodeScalar`值(`68`, `111`, `103`)的`value`属性仍然代表字符`D`、`o`和`g`。
|
||||
第四个`codeUnit`值(`8252`)仍然是一个等于十六进制`203C`的十进制值。这个代表了`DOUBLE EXCLAMATION MARK`字符的 Unicode 标量`U+203C`。
|
||||
|
||||
第五位数值,128054,是一个十六进制1F436的十进制表示。其等同于`DOG FACE`的Unicode 标量`U+1F436`。
|
||||
第五个`UnicodeScalar`值的`value`属性,`128054`,是一个十六进制`1F436`的十进制表示。其等同于`DOG FACE`的 Unicode 标量`U+1F436`。
|
||||
|
||||
作为查询字符值属性的一种替代方法,每个`UnicodeScalar`值也可以用来构建一个新的字符串值,比如在字符串插值中使用:
|
||||
作为查询它们的`value`属性的一种替代方法,每个`UnicodeScalar`值也可以用来构建一个新的`String`值,比如在字符串插值中使用:
|
||||
|
||||
```swift
|
||||
for scalar in dogString.unicodeScalars {
|
||||
|
||||
@ -21,7 +21,7 @@ Swift 语言提供`Arrays`、`Sets`和`Dictionaries`三种基本的集合类型
|
||||
|
||||
Swift 语言中的`Arrays`、`Sets`和`Dictionaries`中存储的数据值类型必须明确。这意味着我们不能把不正确的数据类型插入其中。同时这也说明我们完全可以对取回值的类型非常自信。
|
||||
|
||||
> 注意:
|
||||
> 注意:
|
||||
Swift 的`Arrays`、`Sets`和`Dictionaries`类型被实现为泛型集合。更多关于泛型类型和集合,参见 [泛型](./23_Generics.html)章节。
|
||||
|
||||
<a name="mutability_of_collections"></a>
|
||||
@ -29,8 +29,8 @@ Swift 的`Arrays`、`Sets`和`Dictionaries`类型被实现为泛型集合。更
|
||||
|
||||
如果创建一个`Arrays`、`Sets`或`Dictionaries`并且把它分配成一个变量,这个集合将会是可变的。这意味着我们可以在创建之后添加更多或移除已存在的数据项来改变这个集合的大小。如果我们把`Arrays`、`Sets`或`Dictionaries`分配成常量,那么它就是不可变的,它的大小不能被改变。
|
||||
|
||||
> 注意:
|
||||
在我们不需要改变集合大小的时候创建不可变集合是很好的习惯。如此 Swift 编译器可以优化我们创建的集合。
|
||||
> 注意:
|
||||
在我们不需要改变集合大小的时候创建不可变集合是很好的习惯。如此 Swift 编译器可以优化我们创建的集合。
|
||||
|
||||
<a name="arrays"></a>
|
||||
## 数组(Arrays)
|
||||
@ -44,57 +44,58 @@ Swift 的`Arrays`、`Sets`和`Dictionaries`类型被实现为泛型集合。更
|
||||
<a name="array_type_shorthand_syntax"></a>
|
||||
### 数组的简单语法
|
||||
|
||||
写 Swift 数组应该遵循像`Array<T>`这样的形式,其中`T`是这个数组中唯一允许存在的数据类型。我们也可以使用像`[T]`这样的简单语法。尽管两种形式在功能上是一样的,但是推荐较短的那种,而且在本文中都会使用这种形式来使用数组。
|
||||
写 Swift 数组应该遵循像`Array<Element>`这样的形式,其中`Element`是这个数组中唯一允许存在的数据类型。我们也可以使用像`[Element]`这样的简单语法。尽管两种形式在功能上是一样的,但是推荐较短的那种,而且在本文中都会使用这种形式来使用数组。
|
||||
|
||||
<a name="creating_an_empty_array"></a>
|
||||
###创建一个空数组
|
||||
### 创建一个空数组
|
||||
|
||||
我们可以使用构造语法来创建一个由特定数据类型构成的空数组:
|
||||
|
||||
```swift
|
||||
var someInts = [Int]()
|
||||
print("someInts is of type [Int] with \(someInts.count) items。")
|
||||
// 打印 "someInts is of type [Int] with 0 items。"
|
||||
print("someInts is of type [Int] with \(someInts.count) items.")
|
||||
// 打印 "someInts is of type [Int] with 0 items."
|
||||
```
|
||||
|
||||
注意 通过构造函数的类型,`someInts`的值类型被推断为`[Int]`。
|
||||
注意,通过构造函数的类型,`someInts`的值类型被推断为`[Int]`。
|
||||
|
||||
或者,如果代码上下文中已经提供了类型信息,例如一个函数参数或者一个已经定义好类型的常量或者变量,我们可以使用空数组语句创建一个空数组,它的写法很简单:`[]`(一对空方括号):
|
||||
|
||||
```swift
|
||||
someInts.append(3)
|
||||
// someInts 现在包含一个Int值
|
||||
// someInts 现在包含一个 Int 值
|
||||
someInts = []
|
||||
// someInts 现在是空数组,但是仍然是[Int]类型的。
|
||||
// someInts 现在是空数组,但是仍然是 [Int] 类型的。
|
||||
```
|
||||
|
||||
<a name="creating_an_array_with_a_default_value"></a>
|
||||
###创建一个带有默认值的数组
|
||||
### 创建一个带有默认值的数组
|
||||
|
||||
Swift 中的`Array`类型还提供一个可以创建特定大小并且所有数据都被默认的构造方法。我们可以把准备加入新数组的数据项数量(`count`)和适当类型的初始值(`repeatedValue`)传入数组构造函数:
|
||||
|
||||
```swift
|
||||
var threeDoubles = [Double](count: 3, repeatedValue:0.0)
|
||||
// threeDoubles 是一种 [Double]数组, 等于 [0.0, 0.0, 0.0]
|
||||
// threeDoubles 是一种 [Double] 数组,等价于 [0.0, 0.0, 0.0]
|
||||
```
|
||||
|
||||
<a name="creating_an_array_by_adding_two_arrays_together"></a>
|
||||
###通过两个数组相加创建一个数组
|
||||
### 通过两个数组相加创建一个数组
|
||||
|
||||
我们可以使用加法操作符(`+`)来组合两种已存在的相同类型数组。新数组的数据类型会被从两个数组的数据类型中推断出来:
|
||||
|
||||
```swift
|
||||
var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5)
|
||||
// anotherThreeDoubles is inferred as [Double], and equals [2.5, 2.5, 2.5]
|
||||
// anotherThreeDoubles 被推断为 [Double],等价于 [2.5, 2.5, 2.5]
|
||||
|
||||
var sixDoubles = threeDoubles + anotherThreeDoubles
|
||||
// sixDoubles 被推断为 [Double], 等于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
|
||||
// sixDoubles 被推断为 [Double],等价于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
|
||||
```
|
||||
|
||||
<a name="creating_an_array_with_an_array_literals"></a>
|
||||
### 用字面量构造数组
|
||||
|
||||
我们可以使用字面量来进行数组构造,这是一种用一个或者多个数值构造数组的简单方法。字面量是一系列由逗号分割并由方括号包含的数值。
|
||||
我们可以使用字面量来进行数组构造,这是一种用一个或者多个数值构造数组的简单方法。字面量是一系列由逗号分割并由方括号包含的数值:
|
||||
|
||||
`[value 1, value 2, value 3]`。
|
||||
|
||||
下面这个例子创建了一个叫做`shoppingList`并且存储`String`的数组:
|
||||
@ -106,10 +107,10 @@ var shoppingList: [String] = ["Eggs", "Milk"]
|
||||
|
||||
`shoppingList`变量被声明为“字符串值类型的数组“,记作`[String]`。 因为这个数组被规定只有`String`一种数据结构,所以只有`String`类型可以在其中被存取。 在这里,`shoppinglist`数组由两个`String`值(`"Eggs"` 和`"Milk"`)构造,并且由字面量定义。
|
||||
|
||||
> 注意:
|
||||
> `Shoppinglist`数组被声明为变量(`var`关键字创建)而不是常量(`let`创建)是因为以后可能会有更多的数据项被插入其中。
|
||||
> 注意:
|
||||
`Shoppinglist`数组被声明为变量(`var`关键字创建)而不是常量(`let`创建)是因为以后可能会有更多的数据项被插入其中。
|
||||
|
||||
在这个例子中,字面量仅仅包含两个`String`值。匹配了该数组的变量声明(只能包含`String`的数组),所以这个字面量的分配过程就是允许用两个初始项来构造`shoppinglist`。
|
||||
在这个例子中,字面量仅仅包含两个`String`值。匹配了该数组的变量声明(只能包含`String`的数组),所以这个字面量的分配过程可以作为用两个初始项来构造`shoppinglist`的一种方式。
|
||||
|
||||
由于 Swift 的类型推断机制,当我们用字面量构造只拥有相同类型值数组的时候,我们不必把数组的类型定义清楚。 `shoppinglist`的构造也可以这样写:
|
||||
|
||||
@ -122,15 +123,16 @@ var shoppingList = ["Eggs", "Milk"]
|
||||
<a name="accessing_and_modifying_an_array"></a>
|
||||
### 访问和修改数组
|
||||
|
||||
我们可以通过数组的方法和属性来访问和修改数组,或者下标语法。
|
||||
还可以使用数组的只读属性`count`来获取数组中的数据项数量。
|
||||
我们可以通过数组的方法和属性来访问和修改数组,或者使用下标语法。
|
||||
|
||||
可以使用数组的只读属性`count`来获取数组中的数据项数量:
|
||||
|
||||
```swift
|
||||
print("The shopping list contains \(shoppingList.count) items.")
|
||||
// 输出"The shopping list contains 2 items."(这个数组有2个项)
|
||||
// 输出 "The shopping list contains 2 items."(这个数组有2个项)
|
||||
```
|
||||
|
||||
使用布尔项`isEmpty`来作为检查`count`属性的值是否为 0 的捷径。
|
||||
使用布尔值属性`isEmpty`作为检查`count`属性的值是否为 0 的捷径:
|
||||
|
||||
```swift
|
||||
if shoppingList.isEmpty {
|
||||
@ -138,10 +140,10 @@ if shoppingList.isEmpty {
|
||||
} else {
|
||||
print("The shopping list is not empty.")
|
||||
}
|
||||
// 打印 "The shopping list is not empty."(shoppinglist不是空的)
|
||||
// 打印 "The shopping list is not empty."(shoppinglist 不是空的)
|
||||
```
|
||||
|
||||
也可以使用`append`方法在数组后面添加新的数据项:
|
||||
也可以使用`append(_:)`方法在数组后面添加新的数据项:
|
||||
|
||||
```swift
|
||||
shoppingList.append("Flour")
|
||||
@ -153,7 +155,7 @@ shoppingList.append("Flour")
|
||||
```swift
|
||||
shoppingList += ["Baking Powder"]
|
||||
// shoppingList 现在有四项了
|
||||
shoppingList += ["Chocolate Spread","Cheese","Butter"]
|
||||
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
|
||||
// shoppingList 现在有七项了
|
||||
```
|
||||
|
||||
@ -164,7 +166,8 @@ var firstItem = shoppingList[0]
|
||||
// 第一项是 "Eggs"
|
||||
```
|
||||
|
||||
注意第一项在数组中的索引值是`0`而不是`1`。 Swift 中的数组索引总是从零开始。
|
||||
> 注意:
|
||||
第一项在数组中的索引值是`0`而不是`1`。 Swift 中的数组索引总是从零开始。
|
||||
|
||||
我们也可以用下标来改变某个已有索引值对应的数据值:
|
||||
|
||||
@ -177,11 +180,11 @@ shoppingList[0] = "Six eggs"
|
||||
|
||||
```swift
|
||||
shoppingList[4...6] = ["Bananas", "Apples"]
|
||||
// shoppingList 现在有六项
|
||||
// shoppingList 现在有6项
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 不可以用下标访问的形式去在数组尾部添加新项。
|
||||
> 注意:
|
||||
不可以用下标访问的形式去在数组尾部添加新项。
|
||||
|
||||
|
||||
调用数组的`insert(_:atIndex:)`方法来在某个具体索引值之前添加数据项:
|
||||
@ -192,33 +195,33 @@ shoppingList.insert("Maple Syrup", atIndex: 0)
|
||||
// "Maple Syrup" 现在是这个列表中的第一项
|
||||
```
|
||||
|
||||
这次`insert`函数调用把值为`"Maple Syrup"`的新数据项插入列表的最开始位置,并且使用`0`作为索引值。
|
||||
这次`insert(_:atIndex:)`方法调用把值为`"Maple Syrup"`的新数据项插入列表的最开始位置,并且使用`0`作为索引值。
|
||||
|
||||
类似的我们可以使用`removeAtIndex`方法来移除数组中的某一项。这个方法把数组在特定索引值中存储的数据项移除并且返回这个被移除的数据项(我们不需要的时候就可以无视它):
|
||||
类似的我们可以使用`removeAtIndex(_:)`方法来移除数组中的某一项。这个方法把数组在特定索引值中存储的数据项移除并且返回这个被移除的数据项(我们不需要的时候就可以无视它):
|
||||
|
||||
```swift
|
||||
let mapleSyrup = shoppingList.removeAtIndex(0)
|
||||
// 索引值为0的数据项被移除
|
||||
// shoppingList 现在只有6项,而且不包括Maple Syrup
|
||||
// mapleSyrup常量的值等于被移除数据项的值 "Maple Syrup"
|
||||
// shoppingList 现在只有6项,而且不包括 Maple Syrup
|
||||
// mapleSyrup 常量的值等于被移除数据项的值 "Maple Syrup"
|
||||
```
|
||||
> 注意:
|
||||
> 如果我们试着对索引越界的数据进行检索或者设置新值的操作,会引发一个运行期错误。我们可以使用索引值和数组的`count`属性进行比较来在使用某个索引之前先检验是否有效。除了当`count`等于 0 时(说明这是个空数组),最大索引值一直是`count - 1`,因为数组都是零起索引。
|
||||
> 注意:
|
||||
如果我们试着对索引越界的数据进行检索或者设置新值的操作,会引发一个运行期错误。我们可以使用索引值和数组的`count`属性进行比较来在使用某个索引之前先检验是否有效。除了当`count`等于 0 时(说明这是个空数组),最大索引值一直是`count - 1`,因为数组都是零起索引。
|
||||
|
||||
数据项被移除后数组中的空出项会被自动填补,所以现在索引值为`0`的数据项的值再次等于`"Six eggs"`:
|
||||
数据项被移除后数组中的空出项会被自动填补,所以现在索引值为`0`的数据项的值再次等于`"Six eggs"`:
|
||||
|
||||
```swift
|
||||
firstItem = shoppingList[0]
|
||||
// firstItem 现在等于 "Six eggs"
|
||||
```
|
||||
|
||||
如果我们只想把数组中的最后一项移除,可以使用`removeLast`方法而不是`removeAtIndex(_:)`方法来避免我们需要获取数组的`count`属性。就像后者一样,前者也会返回被移除的数据项:
|
||||
如果我们只想把数组中的最后一项移除,可以使用`removeLast()`方法而不是`removeAtIndex(_:)`方法来避免我们需要获取数组的`count`属性。就像后者一样,前者也会返回被移除的数据项:
|
||||
|
||||
```swift
|
||||
let apples = shoppingList.removeLast()
|
||||
// 数组的最后一项被移除了
|
||||
// shoppingList现在只有5项,不包括cheese
|
||||
// apples 常量的值现在等于"Apples" 字符串
|
||||
// shoppingList 现在只有5项,不包括 cheese
|
||||
// apples 常量的值现在等于 "Apples" 字符串
|
||||
```
|
||||
|
||||
<a name="iterating_over_an_array"></a>
|
||||
@ -255,36 +258,37 @@ for (index, value) in shoppingList.enumerate() {
|
||||
<a name="sets"></a>
|
||||
## 集合
|
||||
|
||||
集合(Set)用来存储相同类型并且没有确定顺序的值。当集合元素顺序不重要时或者希望确保每个元素只出现一次时可以把集合当做是数组另一形式。
|
||||
集合(Set)用来存储相同类型并且没有确定顺序的值。当集合元素顺序不重要时或者希望确保每个元素只出现一次时可以使用集合而不是数组。
|
||||
|
||||
> 注意:
|
||||
> Swift的`Set`类型被桥接到`Fundation`中的`NSSet`类。
|
||||
> 注意:
|
||||
> Swift的`Set`类型被桥接到`Fundation`中的`NSSet`类。
|
||||
> 关于使用`Fundation`和`Cocoa`中`Set`的知识,请看 [*Using Swift with Cocoa and Objective-C*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216)。
|
||||
|
||||
<a name="hash_values_for_set_types"></a>
|
||||
#### Set类型的哈希值
|
||||
#### 集合类型的哈希值
|
||||
|
||||
为了存储在集合中,该类型必须是可哈希化的-也就是说,该类型必须提供一个方法来计算它的哈希值。一个哈希值是```Int```类型的,它和其他的对象相同,其被用来比较相等与否,比如```a==b```,它遵循的是```a.hashValue == b.hashValue```。
|
||||
一个类型为了存储在集合中,该类型必须是可哈希化的--也就是说,该类型必须提供一个方法来计算它的哈希值。一个哈希值是`Int`类型的,相等的对象哈希值必须相同,比如`a==b`,因此必须`a.hashValue == b.hashValue`。
|
||||
|
||||
Swift 的所有基本类型(比如```String```,```Int```,```Double```和```Bool```)默认都是可哈希化的,它可以作为集合的值或者字典的键值类型。没有关联值的枚举成员值(在[枚举](./08_Enumerations.html)有讲述)默认也是可哈希化的。
|
||||
Swift 的所有基本类型(比如`String`,`Int`,`Double`和`Bool`)默认都是可哈希化的,可以作为集合的值的类型或者字典的键的类型。没有关联值的枚举成员值(在[枚举](./08_Enumerations.html)有讲述)默认也是可哈希化的。
|
||||
|
||||
> 注意:
|
||||
> 你可以使用你自定义的类型作为集合的值或者是字典的键值类型,但你需要使你的自定义类型服从Swift标准库中的`Hashable`协议。服从`Hashable`协议的类型需要提供一个类型为`Int`的取值访问器属性`hashValue`。这个由类型的`hashValue`返回的值不需要在同一程序的不同执行周期或者不同程序之间保持相同。
|
||||
> 因为`hashable`协议服从于`Equatable`协议,所以遵循该协议的类型也必须提供一个"是否等"运算符(`==`)的实现。这个`Equatable`协议需要任何遵循的`==`的实现都是一种相等的关系。也就是说,对于`a,b,c`三个值来说,`==`的实现必须满足下面三种情况:
|
||||
> 注意:
|
||||
> 你可以使用你自定义的类型作为集合的值的类型或者是字典的键的类型,但你需要使你的自定义类型符合 Swift 标准库中的`Hashable`协议。符合`Hashable`协议的类型需要提供一个类型为`Int`的可读属性`hashValue`。由类型的`hashValue`属性返回的值不需要在同一程序的不同执行周期或者不同程序之间保持相同。
|
||||
|
||||
* ```a==a```(自反性)
|
||||
* ```a==b```意味着```b==a```(对称性)
|
||||
* ```a==b&&b==c```意味着```a==c```(传递性)
|
||||
> 因为`Hashable`协议符合`Equatable`协议,所以符合该协议的类型也必须提供一个"是否相等"运算符(`==`)的实现。这个`Equatable`协议要求任何符合`==`实现的实例间都是一种相等的关系。也就是说,对于`a,b,c`三个值来说,`==`的实现必须满足下面三种情况:
|
||||
|
||||
关于协议遵循的更多信息,请看[协议](./22_Protocols.html)
|
||||
> * `a == a`(自反性)
|
||||
> * `a == b`意味着`b == a`(对称性)
|
||||
> * `a == b && b == c`意味着`a == c`(传递性)
|
||||
|
||||
关于符合协议的更多信息,请看[协议](./22_Protocols.html)。
|
||||
|
||||
<a name="set_type_syntax"></a>
|
||||
### Set类型语法
|
||||
### 集合类型语法
|
||||
|
||||
Swift中的`Set`类型被写为`Set<T>`, 这里的`T`表示`Set`中允许存储的类型,和数组不同的是,集合没有等价的简化形式。
|
||||
Swift 中的`Set`类型被写为`Set<Element>`,这里的`Element`表示`Set`中允许存储的类型,和数组不同的是,集合没有等价的简化形式。
|
||||
|
||||
<a name="creating_and_initalizing_an_empty_set"></a>
|
||||
### 创建和构造一个空的Set
|
||||
### 创建和构造一个空的集合
|
||||
|
||||
你可以通过构造器语法创建一个特定类型的空集合:
|
||||
|
||||
@ -294,16 +298,16 @@ print("letters is of type Set<Character> with \(letters.count) items.")
|
||||
// 打印 "letters is of type Set<Character> with 0 items."
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 注意:
|
||||
> 通过构造器,这里的`letters`变量的类型被推断为`Set<Character>`。
|
||||
|
||||
此外,如果上下文提供了类型信息,比如作为函数的参数或者已知类型的变量或常量,我们可以通过一个空的数组字面量创建一个空的`Set`:
|
||||
|
||||
```swift
|
||||
letters.insert("a")
|
||||
// letters 现在含有1个Character类型的值
|
||||
// letters 现在含有1个 Character 类型的值
|
||||
letters = []
|
||||
// letters 现在是一个空的Set, 但是它依然是 Set<Character> 类型
|
||||
// letters 现在是一个空的 Set, 但是它依然是 Set<Character> 类型
|
||||
```
|
||||
|
||||
<a name="creating_a_set_with_an_array_literal"></a>
|
||||
@ -315,35 +319,35 @@ letters = []
|
||||
|
||||
```swift
|
||||
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
|
||||
// favoriteGenres被构造成含有三个初始值的集合
|
||||
// favoriteGenres 被构造成含有三个初始值的集合
|
||||
```
|
||||
|
||||
这个`favoriteGenres`变量被声明为“一个`String`值的集合”,写为`Set<String>`。由于这个特定的集合含有指定`String`类型的值,所以它只允许存储`String`类型值。这里的`favoriteGenres`变量有三个`String`类型的初始值("`Rock`","`Classical`"和"`Hip hop`"),并以数组字面量的方式出现。
|
||||
这个`favoriteGenres`变量被声明为“一个`String`值的集合”,写为`Set<String>`。由于这个特定的集合含有指定`String`类型的值,所以它只允许存储`String`类型值。这里的`favoriteGenres`变量有三个`String`类型的初始值(`"Rock"`,`"Classical"`和`"Hip hop"`),并以数组字面量的方式出现。
|
||||
|
||||
> 注意:
|
||||
> 注意:
|
||||
> `favoriteGenres`被声明为一个变量(拥有`var`标示符)而不是一个常量(拥有`let`标示符),因为它里面的元素将会在下面的例子中被增加或者移除。
|
||||
|
||||
一个`Set`类型不能从数组中字面量中独立地被推断出来,因此`Set`类型必须显式声明。然而,由于Swift的类型推导功能,如果你想使用一个数组字面量构造一个Set并且该数组字面量中的所有元素类型相同,那么你无须写出`Set`的具体类型。`favoriteGenres`的构造形式可以采用简化的方式代替:
|
||||
一个`Set`类型不能从数组字面量中被单独推断出来,因此`Set`类型必须显式声明。然而,由于 Swift 的类型推断功能,如果你想使用一个数组字面量构造一个`Set`并且该数组字面量中的所有元素类型相同,那么你无须写出`Set`的具体类型。`favoriteGenres`的构造形式可以采用简化的方式代替:
|
||||
|
||||
```swift
|
||||
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
|
||||
```
|
||||
|
||||
由于数组字面量中的所有元素类型相同,Swift可以推断出`Set<String>`作为`favoriteGenres`变量的正确类型。
|
||||
由于数组字面量中的所有元素类型相同,Swift 可以推断出`Set<String>`作为`favoriteGenres`变量的正确类型。
|
||||
|
||||
<a name="accesing_and_modifying_a_set"></a>
|
||||
### 访问和修改一个Set
|
||||
### 访问和修改一个集合
|
||||
|
||||
你可以通过`Set`的属性和方法来访问和修改一个`Set`.
|
||||
你可以通过`Set`的属性和方法来访问和修改一个`Set`。
|
||||
|
||||
为了找出一个`Set`中元素的数量,可以使用其只读属性`count`:
|
||||
为了找出一个`Set`中元素的数量,可以使用其只读属性`count`:
|
||||
|
||||
```swift
|
||||
print("I have \(favoriteGenres.count) favorite music genres.")
|
||||
// 打印 "I have 3 favorite music genres."
|
||||
```
|
||||
|
||||
使用布尔属性`isEmpty`作为一个缩写形式去检查`count`属性是否为`0`:
|
||||
使用布尔属性`isEmpty`作为一个缩写形式去检查`count`属性是否为`0`:
|
||||
|
||||
```swift
|
||||
if favoriteGenres.isEmpty {
|
||||
@ -354,14 +358,14 @@ if favoriteGenres.isEmpty {
|
||||
// 打印 "I have particular music preferences."
|
||||
```
|
||||
|
||||
你可以通过调用`Set`的` insert(_:) `方法来添加一个新元素:
|
||||
你可以通过调用`Set`的`insert(_:)`方法来添加一个新元素:
|
||||
|
||||
```swift
|
||||
favoriteGenres.insert("Jazz")
|
||||
// favoriteGenres 现在包含4个元素
|
||||
```
|
||||
|
||||
你可以通过调用`Set`的`remove(_:)`方法去删除一个元素,如果该值是该`Set`的一个元素则删除该元素并且返回被删除的元素值,否认如果该`Set`不包含该值,则返回`nil`。另外,`Set`中的所有元素可以通过它的`removeAll()`方法删除。
|
||||
你可以通过调用`Set`的`remove(_:)`方法去删除一个元素,如果该值是该`Set`的一个元素则删除该元素并且返回被删除的元素值,否则如果该`Set`不包含该值,则返回`nil`。另外,`Set`中的所有元素可以通过它的`removeAll()`方法删除。
|
||||
|
||||
```swift
|
||||
if let removedGenre = favoriteGenres.remove("Rock") {
|
||||
@ -372,7 +376,7 @@ if let removedGenre = favoriteGenres.remove("Rock") {
|
||||
// 打印 "Rock? I'm over it."
|
||||
```
|
||||
|
||||
使用```contains(_:)```方法去检查```Set```中是否包含一个特定的值。
|
||||
使用`contains(_:)`方法去检查`Set`中是否包含一个特定的值:
|
||||
|
||||
```swift
|
||||
if favoriteGenres.contains("Funk") {
|
||||
@ -384,9 +388,9 @@ if favoriteGenres.contains("Funk") {
|
||||
```
|
||||
|
||||
<a name="iterating_over_a_set"></a>
|
||||
### 遍历一个Set
|
||||
### 遍历一个集合
|
||||
|
||||
你可以在一个```for-in```循环中遍历一个```Set```中的所有值。
|
||||
你可以在一个`for-in`循环中遍历一个`Set`中的所有值。
|
||||
|
||||
```swift
|
||||
for genre in favoriteGenres {
|
||||
@ -397,9 +401,9 @@ for genre in favoriteGenres {
|
||||
// Hip hop
|
||||
```
|
||||
|
||||
更多关于`for-in`循环信息,参见[For循环](./05_Control_Flow.html#for_loops)。
|
||||
更多关于`for-in`循环的信息,参见[For 循环](./05_Control_Flow.html#for_loops)。
|
||||
|
||||
Swift 的`Set`类型没有确定的顺序,为了按照特定顺序来遍历一个`Set`中值可以使用`sort()`方法,它将根据提供的序列返回一个排序的集合.
|
||||
Swift 的`Set`类型没有确定的顺序,为了按照特定顺序来遍历一个`Set`中的值可以使用`sort()`方法,它将根据提供的序列返回一个有序集合.
|
||||
|
||||
```swift
|
||||
for genre in favoriteGenres.sort() {
|
||||
@ -413,7 +417,7 @@ for genre in favoriteGenres.sort() {
|
||||
<a name="performing_set_operations"></a>
|
||||
### 完成集合操作
|
||||
|
||||
你可以高效的完成`Set`的一些基本操作,比如把两个集合组合到一起,判断两个集合共有元素,或者判断两个集合是否全包含,部分包含或者不相交。
|
||||
你可以高效地完成`Set`的一些基本操作,比如把两个集合组合到一起,判断两个集合共有元素,或者判断两个集合是否全包含,部分包含或者不相交。
|
||||
|
||||
<a name="fundamental_set_operations"></a>
|
||||
#### 基本集合操作
|
||||
@ -423,7 +427,7 @@ for genre in favoriteGenres.sort() {
|
||||

|
||||
|
||||
* 使用`intersect(_:)`方法根据两个集合中都包含的值创建的一个新的集合。
|
||||
* 使用`exclusiveOr(_:)`方法根据值在一个集合中但不在两个集合中的值创建一个新的集合。
|
||||
* 使用`exclusiveOr(_:)`方法根据在一个集合中但不在两个集合中的值创建一个新的集合。
|
||||
* 使用`union(_:)`方法根据两个集合的值创建一个新的集合。
|
||||
* 使用`subtract(_:)`方法根据不在该集合中的值创建一个新的集合。
|
||||
|
||||
@ -431,6 +435,7 @@ for genre in favoriteGenres.sort() {
|
||||
let oddDigits: Set = [1, 3, 5, 7, 9]
|
||||
let evenDigits: Set = [0, 2, 4, 6, 8]
|
||||
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
|
||||
|
||||
oddDigits.union(evenDigits).sort()
|
||||
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
oddDigits.intersect(evenDigits).sort()
|
||||
@ -444,20 +449,21 @@ oddDigits.exclusiveOr(singleDigitPrimeNumbers).sort()
|
||||
<a name="set_membership_and_equality"></a>
|
||||
#### 集合成员关系和相等
|
||||
|
||||
下面的插图描述了三个集合-`a`,`b`和`c`,以及通过悬浮区域表述集合间共享的元素。Set `a`是Set`b`的父集合,因为`a`包含了`b`中所有的元素,相反的,Set `b`是`a`的子集合,因为属于`b`的元素也被`a`包含。Set `b`和Set `c`彼此不关联,因为它们之间没有共同的元素。
|
||||
下面的插图描述了三个集合-`a`,`b`和`c`,以及通过重叠区域表述集合间共享的元素。集合`a`是集合`b`的父集合,因为`a`包含了`b`中所有的元素,相反的,集合`b`是集合`a`的子集合,因为属于`b`的元素也被`a`包含。集合`b`和集合`c`彼此不关联,因为它们之间没有共同的元素。
|
||||
|
||||

|
||||
|
||||
* 使用“是否等”运算符(`==`)来判断两个集合是否包含全部相同的值。
|
||||
* 使用“是否相等”运算符(`==`)来判断两个集合是否包含全部相同的值。
|
||||
* 使用`isSubsetOf(_:)`方法来判断一个集合中的值是否也被包含在另外一个集合中。
|
||||
* 使用`isSupersetOf(_:)`方法来判断一个集合中包含的值是另一个集合中所有的值。
|
||||
* 使用`isStrictSubsetOf(_:)`或者`isStrictSupersetOf(_:)`方法来判断一个集合是否是另外一个集合的子集合或者父集合并且和特定集合不相等。
|
||||
* 使用`isDisjointWith(_:)`方法来判断两个结合是否不含有相同的值。
|
||||
* 使用`isSupersetOf(_:)`方法来判断一个集合中包含另一个集合中所有的值。
|
||||
* 使用`isStrictSubsetOf(_:)`或者`isStrictSupersetOf(_:)`方法来判断一个集合是否是另外一个集合的子集合或者父集合并且两个集合并不相等。
|
||||
* 使用`isDisjointWith(_:)`方法来判断两个集合是否不含有相同的值。
|
||||
|
||||
```swift
|
||||
let houseAnimals: Set = ["🐶", "🐱"]
|
||||
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
|
||||
let cityAnimals: Set = ["🐦", "🐭"]
|
||||
|
||||
houseAnimals.isSubsetOf(farmAnimals)
|
||||
// true
|
||||
farmAnimals.isSupersetOf(houseAnimals)
|
||||
@ -466,32 +472,24 @@ farmAnimals.isDisjointWith(cityAnimals)
|
||||
// true
|
||||
```
|
||||
|
||||
|
||||
|
||||
<a name="dictionaries"></a>
|
||||
## 字典
|
||||
|
||||
字典是一种存储多个相同类型的值的容器。每个值(value)都关联唯一的键(key),键作为字典中的这个值数据的标识符。和数组中的数据项不同,字典中的数据项并没有具体顺序。我们在需要通过标识符(键)访问数据的时候使用字典,这种方法很大程度上和我们在现实世界中使用字典查字义的方法一样。
|
||||
|
||||
> 注意:
|
||||
> Swiftly 的`Dictionary` 类型被桥接到Foundation的`NSDictionary`类。
|
||||
> 注意:
|
||||
> Swift 的`Dictionary`类型被桥接到`Foundation`的`NSDictionary`类。
|
||||
> 更多关于在`Foundation`和`Cocoa`中使用`Dictionary`类型的信息,参见 [*Using Swift with Cocoa and Obejective-C*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 一书。
|
||||
|
||||
<a name="dictionary_type_shorthand_syntax"></a>
|
||||
## 字典类型快捷语法
|
||||
<!--more
|
||||
Swift 的字典使用时需要具体规定可以存储键和值类型。不同于 Objective-C 的`NSDictionary`和`NSMutableDictionary` 类可以使用任何类型的对象来作键和值并且不提供任何关于这些对象的本质信息。在 Swift 中,在某个特定字典中可以存储的键和值必须提前定义清楚,方法是通过显性类型标注或者类型推断。
|
||||
-->
|
||||
|
||||
Swift 的字典使用`Dictionary<Key, Value>`定义,其中`Key`是字典中键的数据类型,`Value`是字典中对应于这些键所存储值的数据类型。
|
||||
|
||||
> 注意:
|
||||
> 注意:
|
||||
> 一个字典的`Key`类型必须遵循`Hashable`协议,就像`Set`的值类型。
|
||||
|
||||
我们也可以用`[Key: Value]`这样快捷的形式去创建一个字典类型。虽然这俩种形式功能上相同,但是后者是首选,并且这本指导书涉及到字典类型时通篇采用后者。
|
||||
|
||||
<!--more
|
||||
`KeyType`的唯一限制就是可哈希的,这样可以保证它是独一无二的,所有的 Swift 基本类型(例如`String`,`Int`, `Double`和`Bool`)都是默认可哈希的,并且所有这些类型都可以在字典中当做键使用。未关联值的枚举成员(参见[枚举](08_Enumerations.html))也是默认可哈希的。
|
||||
-->
|
||||
我们也可以用`[Key: Value]`这样快捷的形式去创建一个字典类型。虽然这两种形式功能上相同,但是后者是首选,并且这本指导书涉及到字典类型时通篇采用后者。
|
||||
|
||||
<a name="creating_an_empty_dictionary"></a>
|
||||
### 创建一个空字典
|
||||
@ -503,22 +501,21 @@ var namesOfIntegers = [Int: String]()
|
||||
// namesOfIntegers 是一个空的 [Int: String] 字典
|
||||
```
|
||||
|
||||
这个例子创建了一个`[Int: String]`类型的空字典来储存英语对整数的命名。它的键是`Int`型,值是`String`型。
|
||||
这个例子创建了一个`[Int: String]`类型的空字典来储存整数的英语命名。它的键是`Int`型,值是`String`型。
|
||||
|
||||
如果上下文已经提供了信息类型,我们可以使用空字典字面量来创建一个空字典,记作`[:]`(中括号中放一个冒号):
|
||||
如果上下文已经提供了类型信息,我们可以使用空字典字面量来创建一个空字典,记作`[:]`(中括号中放一个冒号):
|
||||
|
||||
```swift
|
||||
namesOfIntegers[16] = "sixteen"
|
||||
// namesOfIntegers 现在包含一个键值对
|
||||
namesOfIntegers = [:]
|
||||
// namesOfIntegers 又成为了一个 Int, String类型的空字典
|
||||
// namesOfIntegers 又成为了一个 [Int: String] 类型的空字典
|
||||
```
|
||||
|
||||
<a name="creating_a_dictionary_with_a_dictionary_literal"></a>
|
||||
## 用字典字面量创建字典
|
||||
|
||||
## 字典字面量
|
||||
|
||||
我们可以使用字典字面量来构造字典,这和我们刚才介绍过的数组字面量拥有相似语法。字典字面量是一种将写一个或多个键值对作`Dictionary`集合的快捷途径。
|
||||
我们可以使用字典字面量来构造字典,这和我们刚才介绍过的数组字面量拥有相似语法。字典字面量是一种将一个或多个键值对写作`Dictionary`集合的快捷途径。
|
||||
|
||||
一个键值对是一个`key`和一个`value`的结合体。在字典字面量中,每一个键值对的键和值都由冒号分割。这些键值对构成一个列表,其中这些键值对由方括号包含、由逗号分割:
|
||||
|
||||
@ -532,9 +529,9 @@ namesOfIntegers = [:]
|
||||
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
|
||||
```
|
||||
|
||||
`airports`字典被声明为一种 `[String: String]`类型,这意味着这个字典的键和值都是`String`类型。
|
||||
`airports`字典被声明为一种`[String: String]`类型,这意味着这个字典的键和值都是`String`类型。
|
||||
|
||||
> 注意:
|
||||
> 注意:
|
||||
> `airports`字典被声明为变量(用`var`关键字)而不是常量(`let`关键字)因为后来更多的机场信息会被添加到这个示例字典中。
|
||||
|
||||
`airports`字典使用字典字面量初始化,包含两个键值对。第一对的键是`YYZ`,值是`Toronto Pearson`。第二对的键是`DUB`,值是`Dublin`。
|
||||
@ -542,7 +539,7 @@ var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
|
||||
这个字典语句包含了两个`String: String`类型的键值对。它们对应`airports`变量声明的类型(一个只有`String`键和`String`值的字典)所以这个字典字面量的任务是构造拥有两个初始数据项的`airport`字典。
|
||||
|
||||
和数组一样,我们在用字典字面量构造字典时,如果它的键和值都有各自一致的类型,那么就不必写出字典的类型。
|
||||
`airports`的也可以用这种方法简短定义:
|
||||
`airports`字典也可以用这种简短方式定义:
|
||||
|
||||
```swift
|
||||
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
|
||||
@ -551,9 +548,9 @@ var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
|
||||
因为这个语句中所有的键和值都各自拥有相同的数据类型,Swift 可以推断出`Dictionary<String, String>`是`airports`字典的正确类型。
|
||||
|
||||
<a name="accessing_and_modifying_a_dictionary"></a>
|
||||
### 读取和修改字典
|
||||
### 访问和修改字典
|
||||
|
||||
我们可以通过字典的方法和属性来读取和修改字典,或者通过使用下标语法。
|
||||
我们可以通过字典的方法和属性来访问和修改字典,或者通过使用下标语法。
|
||||
|
||||
和数组一样,我们可以通过字典的只读属性`count`来获取某个字典的数据项数量:
|
||||
|
||||
@ -562,7 +559,7 @@ print("The dictionary of airports contains \(airports.count) items.")
|
||||
// 打印 "The dictionary of airports contains 2 items."(这个字典有两个数据项)
|
||||
```
|
||||
|
||||
使用布尔属性`isEmpty`来快捷的检查字典的`count`属性是否等于0。
|
||||
使用布尔属性`isEmpty`来快捷地检查字典的`count`属性是否等于0:
|
||||
|
||||
```swift
|
||||
if airports.isEmpty {
|
||||
@ -573,7 +570,7 @@ if airports.isEmpty {
|
||||
// 打印 "The airports dictionary is not empty."
|
||||
```
|
||||
|
||||
我们也可以在字典中使用下标语法来添加新的数据项。可以使用一个合适类型的键值作为下标索引,并且分配新的合适类型的值:
|
||||
我们也可以在字典中使用下标语法来添加新的数据项。可以使用一个恰当类型的键作为下标索引,并且分配恰当类型的新值:
|
||||
|
||||
```swift
|
||||
airports["LHR"] = "London"
|
||||
@ -589,7 +586,7 @@ airports["LHR"] = "London Heathrow"
|
||||
|
||||
作为另一种下标方法,字典的`updateValue(_:forKey:)`方法可以设置或者更新特定键对应的值。就像上面所示的下标示例,`updateValue(_:forKey:)`方法在这个键不存在对应值的时候会设置新值或者在存在时更新已存在的值。和上面的下标方法不同的,`updateValue(_:forKey:)`这个方法返回更新值之前的原值。这样使得我们可以检查更新是否成功。
|
||||
|
||||
`updateValue(_:forKey:)`函数会返回包含一个字典值类型的可选值。举例来说:对于存储`String`值的字典,这个函数会返回一个`String?`或者“可选 `String`”类型的值。
|
||||
`updateValue(_:forKey:)`方法会返回对应值的类型的可选值。举例来说:对于存储`String`值的字典,这个函数会返回一个`String?`或者“可选 `String`”类型的值。
|
||||
|
||||
如果有值存在于更新前,则这个可选值包含了旧值,否则它将会是`nil`。
|
||||
|
||||
@ -600,7 +597,7 @@ if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
|
||||
// 输出 "The old value for DUB was Dublin."
|
||||
```
|
||||
|
||||
我们也可以使用下标语法来在字典中检索特定键对应的值。因为有可能请求的键没有对应的值存在,字典的下标访问会返回一个字典值类型的可选值。如果这个字典包含请求键所对应的值,下标会返回一个包含这个存在值的可选值,否则将返回`nil`:
|
||||
我们也可以使用下标语法来在字典中检索特定键对应的值。因为有可能请求的键没有对应的值存在,字典的下标访问会返回对应值的类型的可选值。如果这个字典包含请求键所对应的值,下标会返回一个包含这个存在值的可选值,否则将返回`nil`:
|
||||
|
||||
```swift
|
||||
if let airportName = airports["DUB"] {
|
||||
@ -615,9 +612,9 @@ if let airportName = airports["DUB"] {
|
||||
|
||||
```swift
|
||||
airports["APL"] = "Apple Internation"
|
||||
// "Apple Internation"不是真的 APL机场, 删除它
|
||||
// "Apple Internation" 不是真的 APL 机场, 删除它
|
||||
airports["APL"] = nil
|
||||
// APL现在被移除了
|
||||
// APL 现在被移除了
|
||||
```
|
||||
|
||||
此外,`removeValueForKey(_:)`方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的值或者在没有值的情况下返回`nil`:
|
||||
@ -646,7 +643,7 @@ for (airportCode, airportName) in airports {
|
||||
|
||||
更多关于`for-in`循环的信息,参见[For 循环](./05_Control_Flow.html#for_loops)。
|
||||
|
||||
通过访问`keys`或者`values`属性,我们也可以遍历字典的键或者值。
|
||||
通过访问`keys`或者`values`属性,我们也可以遍历字典的键或者值:
|
||||
|
||||
```swift
|
||||
for airportCode in airports.keys {
|
||||
@ -662,14 +659,14 @@ for airportName in airports.values {
|
||||
// Airport name: London Heathrow
|
||||
```
|
||||
|
||||
如果我们只是需要使用某个字典的键集合或者值集合来作为某个接受`Array`实例 API 的参数,可以直接使用`keys`或者`values`属性直接构造一个新数组:
|
||||
如果我们只是需要使用某个字典的键集合或者值集合来作为某个接受`Array`实例的 API 的参数,可以直接使用`keys`或者`values`属性构造一个新数组:
|
||||
|
||||
```swift
|
||||
let airportCodes = Array(airports.keys)
|
||||
// airportCodes is ["YYZ", "LHR"]
|
||||
let airportCodes = [String](airports.keys)
|
||||
// airportCodes 是 ["YYZ", "LHR"]
|
||||
|
||||
let airportNames = Array(airports.values)
|
||||
// airportNames is ["Toronto Pearson", "London Heathrow"]
|
||||
let airportNames = [String](airports.values)
|
||||
// airportNames 是 ["Toronto Pearson", "London Heathrow"]
|
||||
```
|
||||
|
||||
Swift 的字典类型是无序集合类型。为了以特定的顺序遍历字典的键或值,可以对字典的`keys`或`values`属性使用`sort()`方法。
|
||||
|
||||
@ -550,7 +550,7 @@ case let (x, y):
|
||||
```swift
|
||||
let puzzleInput = "great minds think alike"
|
||||
var puzzleOutput = ""
|
||||
for character in puzzleInput {
|
||||
for character in puzzleInput.characters {
|
||||
switch character {
|
||||
case "a", "e", "i", "o", "u", " ":
|
||||
continue
|
||||
|
||||
@ -40,7 +40,7 @@ func sayHello(personName: String) -> String {
|
||||
|
||||
所有的这些信息汇总起来成为函数的定义,并以 `func` 作为前缀。指定函数返回类型时,用返回箭头 `->`(一个连字符后跟一个右尖括号)后跟返回类型的名称的方式来表示。
|
||||
|
||||
该定义描述了函数做什么,它期望接收什么和执行结束时它返回的结果是什么。这样的定义使得函数可以在别的地方以一种清晰的方式被调用:
|
||||
该定义描述了函数做什么,它期望接收什么和执行结束时它返回的结果是什么类型。这样的定义使得函数可以在别的地方以一种清晰的方式被调用:
|
||||
|
||||
```swift
|
||||
print(sayHello("Anna"))
|
||||
@ -100,9 +100,9 @@ print(sayHelloWorld())
|
||||
|
||||
### 多参量函数 (Functions With Multiple Parameters)
|
||||
|
||||
函数可以有多种输入参数,这写参数被包含在函数的括号之中,以逗号分隔.
|
||||
函数可以有多种输入参数,这些参数被包含在函数的括号之中,以逗号分隔。
|
||||
|
||||
这个函数取得一个人的名字和是否被招呼作为输入,并对那个人返回适当地问候语:
|
||||
这个函数取得一个人的名字和是否被招呼作为输入,并对那个人返回适当的问候语:
|
||||
|
||||
```swift
|
||||
func sayHello(personName: String, alreadyGreeted: Bool) -> String {
|
||||
@ -116,9 +116,9 @@ print(sayHello("Tim", alreadyGreeted: true))
|
||||
// prints "Hello again, Tim!"
|
||||
```
|
||||
|
||||
你通过在括号内传递一个`String`参数值和一个标识为`alreadyGreeted`的`Bool`值,使用逗号分隔来调用`sayHello(_:alreadyGreeted:)`函数.
|
||||
你通过在括号内传递一个`String`参数值和一个标识为`alreadyGreeted`的`Bool`值,使用逗号分隔来调用`sayHello(_:alreadyGreeted:)`函数。
|
||||
|
||||
当调用超过一个参数的函数时,第一个参数后的参数根据其对应的参数名称标记,函数参数命名在[函数参数名称(Function Parameter Names)](#Function_Parameter_Names)有更详细的描述.
|
||||
当调用超过一个参数的函数时,第一个参数后的参数根据其对应的参数名称标记,函数参数命名在[函数参数名称(Function Parameter Names)](#Function_Parameter_Names)有更详细的描述。
|
||||
|
||||
<a name="functions_without_return_values"></a>
|
||||
### 无返回值函数(Functions Without Return Values)
|
||||
@ -164,7 +164,7 @@ printWithoutCounting("hello, world")
|
||||
|
||||
你可以用元组(tuple)类型让多个值作为一个复合值从函数中返回。
|
||||
|
||||
下面的这个例子中,定义了一个名为`minMax(_:)`的函数,作用是在一个`Int`数组中找出最小值与最大值.
|
||||
下面的这个例子中,定义了一个名为`minMax(_:)`的函数,作用是在一个`Int`数组中找出最小值与最大值。
|
||||
|
||||
```swift
|
||||
func minMax(array: [Int]) -> (min: Int, max: Int) {
|
||||
@ -181,9 +181,9 @@ func minMax(array: [Int]) -> (min: Int, max: Int) {
|
||||
}
|
||||
```
|
||||
|
||||
`minMax(_:)`函数返回一个包含两个`Int`值的元组,这些值被标记为`min`和`max`,一遍查询函数的返回值时他们可以被访问.
|
||||
`minMax(_:)`函数返回一个包含两个`Int`值的元组,这些值被标记为`min`和`max`,以便查询函数的返回值时他们可以被访问。
|
||||
|
||||
`minMax(_:)`的函数体中,在开始的时候设置两个工作变量`currentMin`和`currentMax`作为数组中的第一个`Int`值.然后函数会遍历数组中剩余的值并检查该值是否比`currentMin`和`currentMax`更小或更大.最后数组中的最小值与最大值返回两个`Int`值最为一个元组.
|
||||
`minMax(_:)`的函数体中,在开始的时候设置两个工作变量`currentMin`和`currentMax`作为数组中的第一个`Int`值。然后函数会遍历数组中剩余的值并检查该值是否比`currentMin`和`currentMax`更小或更大。最后数组中的最小值与最大值返回两个`Int`值最为一个元组。
|
||||
|
||||
因为元组的成员值被命名为函数的返回类型的一部分,可以通过点语法来访问与取回发现的最小值与最小值:
|
||||
|
||||
@ -197,14 +197,14 @@ print("min is \(bounds.min) and max is \(bounds.max)")
|
||||
|
||||
可选元组返回类型(Optional Tuple Return Types)
|
||||
|
||||
如果函数返回的元组类型中有可能在整个元组中含有“没有值”,你可以使用*可选的(Optional)* 元组返回类型反映整个元组可以是`nil`的事实.你可以通过在元组类型的右括号后放置一个问号来定义一个可选元组,例如`(Int,Int)?`或`(String,Int,Bool)?`
|
||||
如果函数返回的元组类型中有可能在整个元组中含有“没有值”,你可以使用*可选的(Optional)* 元组返回类型反映整个元组可以是`nil`的事实。你可以通过在元组类型的右括号后放置一个问号来定义一个可选元组,例如`(Int,Int)?`或`(String,Int,Bool)?`
|
||||
|
||||
> 注意:
|
||||
> 可选元组类型如`(Int,Int)?`与元组包含可选属性如`(Int?,Int?)`是不同的.可选的元组类型,整个数组是可选的,而不只是元组中的每个元素值.
|
||||
> 可选元组类型如`(Int,Int)?`与元组包含可选属性如`(Int?,Int?)`是不同的.可选的元组类型,整个数组是可选的,而不只是元组中的每个元素值。
|
||||
|
||||
前面的`minMax(_:)`函数返回了一个包含两个`Int`值的元组.但是函数不会在数组中执行任何安全检查,如果`array`参数有一个空数组,如上定义的`minMax(_:)`在试图访问`array[0]`时会触发一个运行时错误.
|
||||
前面的`minMax(_:)`函数返回了一个包含两个`Int`值的元组。但是函数不会在数组中执行任何安全检查,如果`array`参数有一个空数组,如上定义的`minMax(_:)`在试图访问`array[0]`时会触发一个运行时错误。
|
||||
|
||||
为了安全的处理这个"空数组"问题,写一个`minMax(_:)`函数使用可选元组返回类型,并且当数组为空时返回`nil`:
|
||||
为了安全的处理这个"空数组"问题,写一个`minMax(_:)`函数使用可选元组返回类型,并且当数组为空时返回`nil`:
|
||||
|
||||
```swift
|
||||
func minMax(array: [Int]) -> (min: Int, max: Int)? {
|
||||
@ -234,7 +234,7 @@ if let bounds = minMax([8, -6, 2, 109, 3, 71]) {
|
||||
<a name="Function_Parameter_Names"></a>
|
||||
## 函数参数名称(Function Parameter Names)
|
||||
|
||||
函数参数都有一个*外部参数名(external parameter name)*和一个*本地参数名(local parameter name)*.外部参数名用来标记传递给函数调用的参数,本地参数名在实现函数的时候使用.
|
||||
函数参数都有一个*外部参数名(external parameter name)*和一个*本地参数名(local parameter name)*。外部参数名用来标记传递给函数调用的参数,本地参数名在实现函数的时候使用。
|
||||
|
||||
```swift
|
||||
func someFunction(firstParameterName: Int, secondParameterName: Int) {
|
||||
@ -245,12 +245,12 @@ func someFunction(firstParameterName: Int, secondParameterName: Int) {
|
||||
someFunction(1, secondParameterName: 2)
|
||||
```
|
||||
|
||||
一般情况下,第一个参数省略其外部参数名,第二个以后的参数使用其本地参数名作为自己的外部参数名.所有参数需要有不同的本地参数名,但可以共享相同的外部参数名.
|
||||
一般情况下,第一个参数省略其外部参数名,第二个以后的参数使用其本地参数名作为自己的外部参数名。所有参数需要有不同的本地参数名,但可以共享相同的外部参数名。
|
||||
|
||||
<a name="specifying_external_parameter_names"></a>
|
||||
### 指定外部参数名(Specifying External Parameter Names)
|
||||
|
||||
你可以在本地参数名前指定外部参数名,中间以空格分隔.
|
||||
你可以在本地参数名前指定外部参数名,中间以空格分隔。
|
||||
|
||||
```swift
|
||||
func someFunction(externalParameterName localParameterName: Int) {
|
||||
@ -259,10 +259,10 @@ func someFunction(externalParameterName localParameterName: Int) {
|
||||
}
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 注意:
|
||||
> 如果你提供了外部参数名,那么函数在被调用时,必须使用外部参数名。
|
||||
|
||||
这个版本的`sayHello(_:)`函数,得到了两个人的名字,会同时返回对他俩的问候:
|
||||
这个版本的`sayHello(_:)`函数,得到了两个人的名字,会同时返回对他俩的问候:
|
||||
|
||||
```swift
|
||||
func sayHello(to person: String, and anotherPerson: String) -> String {
|
||||
@ -272,13 +272,13 @@ print(sayHello(to: "Bill", and: "Ted"))
|
||||
// prints "Hello Bill and Ted!"
|
||||
```
|
||||
|
||||
为每个参数指定外部参数名,在你调用函数`sayHello(to:and:)`函数时时两个参数都必须被标记出来.
|
||||
为每个参数指定外部参数名,在你调用函数`sayHello(to:and:)`函数时时两个参数都必须被标记出来。
|
||||
|
||||
使用外部函数名可以使得函数可以用一句话表达清楚,并且使得函数体内部可读,能表达出函数的明确意图.
|
||||
使用外部函数名可以使得函数可以用一句话表达清楚,并且使得函数体内部可读,能表达出函数的明确意图。
|
||||
|
||||
### 忽略外部参数名(Omitting External Parameter Names)
|
||||
|
||||
如果你不想为第二个及后续的参数设置参数名,用一个下划线(_)代替一个明确地参数名.
|
||||
如果你不想为第二个及后续的参数设置参数名,用一个下划线(`_`)代替一个明确地参数名。
|
||||
|
||||
```swift
|
||||
func someFunction(firstParameterName: Int, _ secondParameterName: Int) {
|
||||
@ -292,7 +292,7 @@ someFunction(1, 2)
|
||||
> 注意:
|
||||
> 因为第一个参数默认忽略其外部参数名称,明确写下划线是多余的。
|
||||
|
||||
|
||||
<a name="default_parameter_values"></a>
|
||||
### 默认参数值(Default Parameter Values)
|
||||
|
||||
你可以在函数体中为每个参数定义`默认值(Deafult Values)`。当默认值被定义后,调用这个函数时可以忽略这个参数。
|
||||
@ -301,7 +301,7 @@ someFunction(1, 2)
|
||||
func someFunction(parameterWithDefault: Int = 12) {
|
||||
// function body goes here
|
||||
// if no arguments are passed to the function call,
|
||||
// value of parameterWithDefault is 42
|
||||
// value of parameterWithDefault is 12
|
||||
}
|
||||
someFunction(6) // parameterWithDefault is 6
|
||||
someFunction() // parameterWithDefault is 12
|
||||
@ -314,7 +314,7 @@ someFunction() // parameterWithDefault is 12
|
||||
|
||||
一个`可变参数(variadic parameter)`可以接受零个或多个值。函数调用时,你可以用可变参数来传入不确定数量的输入参数。通过在变量类型名后面加入`(...)`的方式来定义可变参数。
|
||||
|
||||
传入可变参数的值在函数体内当做这个类型的一个数组。例如,一个叫做 `numbers` 的 `Double...` 型可变参数,在函数体内可以当做一个叫 `numbers` 的 `Double[]` 型的数组常量。
|
||||
可变参数的传入值在函数体为此类型的一个数组。例如,一个叫做 `numbers` 的 `Double...` 型可变参数,在函数体内可以当做一个叫 `numbers` 的 `[Double]` 型的数组常量。
|
||||
|
||||
下面的这个函数用来计算一组任意长度数字的`算术平均数(arithmetic mean)`:
|
||||
|
||||
@ -333,8 +333,8 @@ arithmeticMean(3, 8.25, 18.75)
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 最多可以有一个可变参数函数,和它必须出现在参数列表中,为了避免歧义在调用函数有多个参数。
|
||||
> 如果你的函数有一个或多个参数有默认值,还有一个可变的参数,将可变参写在参数列表的最后。
|
||||
> 最多可以有一个可变参数函数,和它必须出现在参数列表中,为了避免歧义在调用函数有多个参数。
|
||||
> 如果你的函数有一个或多个参数有默认值,还有一个可变的参数,将可变参写在参数列表的最后。
|
||||
|
||||
如果函数有一个或多个带默认值的参数,而且还有一个可变参数,那么把可变参数放在参数表的最后。
|
||||
|
||||
@ -370,11 +370,11 @@ let paddedString = alignRight(originalString, totalLength: 10, pad: "-")
|
||||
|
||||
函数首先找出有多少字符需要被添加到左边的字符串以右对齐在整个字符串。这个值是存储在一个本地常数称为amountToPad。如果不需要填充(也就是说,如果amountToPad小于1),该函数返回字符串没有填充的输入值。
|
||||
|
||||
否则,该函数创建一个新的临时字符串常量称为padString,初始化填充字符,并将amountToPad padString副本添加到现有的左边的字符串。(一个字符串值不能被添加到一个字符值,所以padString常数用于确保双方+操作符的字符串值)。
|
||||
否则,该函数创建一个新的临时字符串常量称为padString,初始化填充字符,并将amountToPad padString副本添加到现有的左边的字符串。(一个字符串值不能被添加到一个字符值,所以padString常数用于确保双方+操作符的字符串值)。
|
||||
|
||||
该函数首先计算出多少个字符需要被添加到 `string` 的左边,以右对齐到总的字符串中。这个值存在局部常量 `amountToPad` 中。如果不需要填充(即,如果`amountToPad`小于`1`),该函数返回没有填充输入的`string`.
|
||||
该函数首先计算出多少个字符需要被添加到 `string` 的左边,以右对齐到总的字符串中。这个值存在局部常量 `amountToPad` 中。如果不需要填充(即,如果`amountToPad`小于`1`),该函数返回没有填充输入的`string`。
|
||||
|
||||
否则,该函数会建立一个临时的String常量称为`padString`,初始化`pad`字符,并将`amountToPad`作为`padString`的副本添加到现有的字符串左边.(一个`Character`后不能直接添加一个`String`值,所以`padString`经常用于确保`+`号两边都是`String`值.)
|
||||
否则,该函数会建立一个临时的String常量称为`padString`,初始化`pad`字符,并将`amountToPad`作为`padString`的副本添加到现有的字符串左边。(一个`Character`后不能直接添加一个`String`值,所以`padString`经常用于确保`+`号两边都是`String`值。)
|
||||
|
||||
> 注意:
|
||||
> 对变量参数所进行的修改在函数调用结束后便消失了,并且对于函数体外是不可见的。变量参数仅仅存在于函数调用的生命周期中。
|
||||
@ -436,7 +436,7 @@ func multiplyTwoInts(a: Int, _ b: Int) -> Int {
|
||||
|
||||
这个例子中定义了两个简单的数学函数:`addTwoInts` 和 `multiplyTwoInts`。这两个函数都传入两个 `Int` 类型, 返回一个合适的`Int`值。
|
||||
|
||||
这两个函数的类型是 `(Int, Int) -> Int`,可以读作“这个函数类型,它有两个 `Int` 型的参数并返回一个 `Int` 型的值。”。
|
||||
这两个函数的类型是 `(Int, Int) -> Int`,可以读作”这个函数类型,它有两个 `Int` 型的参数并返回一个 `Int` 型的值。”。
|
||||
|
||||
下面是另一个例子,一个没有参数,也没有返回值的函数:
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
- [闭包是引用类型(Closures Are Reference Types)](#closures_are_reference_types)
|
||||
|
||||
闭包是自包含的函数代码块,可以在代码中被传递和使用。
|
||||
Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的 lambdas 函数比较相似。
|
||||
Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的 匿名函数比较相似。
|
||||
|
||||
闭包可以捕获和存储其所在上下文中任意常量和变量的引用。
|
||||
这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。Swift 会为您管理在捕获过程中涉及到的所有内存操作。
|
||||
@ -67,7 +67,7 @@ let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
|
||||
|
||||
该例子对一个`String`类型的数组进行排序,因此排序闭包函数类型需为`(String, String) -> Bool`。
|
||||
|
||||
提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为`ssort(_:)`方法的参数传入:
|
||||
提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为`sort(_:)`方法的参数传入:
|
||||
|
||||
```swift
|
||||
func backwards(s1: String, s2: String) -> Bool {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# 方法(Methods)
|
||||
-----------------
|
||||
|
||||
-----------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[pp-prog](https://github.com/pp-prog)
|
||||
> 校对:[zqp](https://github.com/zqp)
|
||||
@ -96,7 +96,7 @@ counter.incrementBy(5, numberOfTimes: 3)
|
||||
<a name="modifying_external_parameter_name_behavior_for_methods"></a>
|
||||
### 修改方法的外部参数名称(Modifying External Parameter Name Behavior for Methods)
|
||||
|
||||
有时为方法的第一个参数提供一个外部参数名称是非常有用的,尽管这不是默认的行为。你可以自己添加一个显式的外部名称或者用一个井号(`#`)作为第一个参数的前缀来把这个局部名称当作外部名称使用。
|
||||
有时为方法的第一个参数提供一个外部参数名称是非常有用的,尽管这不是默认的行为。你可以自己添加一个显式的外部名称作为第一个参数的前缀来把这个局部名称当作外部名称使用。
|
||||
|
||||
相反,如果你不想为方法的第二个及后续的参数提供一个外部名称,可以通过使用下划线(`_`)作为该参数的显式外部名称,这样做将覆盖默认行为。
|
||||
|
||||
@ -222,7 +222,7 @@ ovenLight.next()
|
||||
|
||||
```swift
|
||||
class SomeClass {
|
||||
class func someTypeMethod() {
|
||||
static func someTypeMethod() {
|
||||
// type method implementation goes here
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# 下标脚本(Subscripts)
|
||||
-----------------
|
||||
|
||||
-----------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[siemenliu](https://github.com/siemenliu)
|
||||
> 校对:[zq54zquan](https://github.com/zq54zquan)
|
||||
@ -92,7 +92,7 @@ numberOfLegs["bird"] = 2
|
||||
|
||||
下标脚本允许任意数量的入参索引,并且每个入参类型也没有限制。下标脚本的返回值也可以是任何类型。下标脚本可以使用变量参数和可变参数,但使用写入读出(in-out)参数或给参数设置默认值都是不允许的。
|
||||
|
||||
一个类或结构体可以根据自身需要提供多个下标脚本实现,在定义下标脚本时通过入参个类型进行区分,使用下标脚本时会自动匹配合适的下标脚本实现运行,这就是*下标脚本的重载*。
|
||||
一个类或结构体可以根据自身需要提供多个下标脚本实现,在定义下标脚本时通过入参的类型进行区分,使用下标脚本时会自动匹配合适的下标脚本实现运行,这就是*下标脚本的重载*。
|
||||
|
||||
一个下标脚本入参是最常见的情况,但只要有合适的场景也可以定义多个下标脚本入参。如下例定义了一个`Matrix`结构体,将呈现一个`Double`类型的二维矩阵。`Matrix`结构体的下标脚本需要两个整型参数:
|
||||
|
||||
|
||||
@ -121,10 +121,10 @@ class Person {
|
||||
|
||||
```swift
|
||||
class Apartment {
|
||||
let number: Int
|
||||
init(number: Int) { self.number = number }
|
||||
let unit: String
|
||||
init(unit: String) { self.unit = unit }
|
||||
var tenant: Person?
|
||||
deinit { print("Apartment #\(number) is being deinitialized") }
|
||||
deinit { print("Apartment \(unit) is being deinitialized") }
|
||||
}
|
||||
```
|
||||
|
||||
@ -134,45 +134,45 @@ class Apartment {
|
||||
|
||||
这两个类都定义了析构函数,用以在类实例被析构的时候输出信息。这让你能够知晓`Person`和`Apartment`的实例是否像预期的那样被销毁。
|
||||
|
||||
接下来的代码片段定义了两个可选类型的变量`john`和`number73`,并分别被设定为下面的`Apartment`和`Person`的实例。这两个变量都被初始化为`nil`,这正是可选的优点:
|
||||
接下来的代码片段定义了两个可选类型的变量`john`和`unit4A`,并分别被设定为下面的`Apartment`和`Person`的实例。这两个变量都被初始化为`nil`,这正是可选的优点:
|
||||
|
||||
```swift
|
||||
var john: Person?
|
||||
var number73: Apartment?
|
||||
var unit4A: Apartment?
|
||||
```
|
||||
|
||||
现在你可以创建特定的`Person`和`Apartment`实例并将赋值给`john`和`number73`变量:
|
||||
现在你可以创建特定的`Person`和`Apartment`实例并将赋值给`john`和`unit4A`变量:
|
||||
|
||||
```swift
|
||||
john = Person(name: "John Appleseed")
|
||||
number73 = Apartment(number: 73)
|
||||
unit4A = Apartment(unit: "4A")
|
||||
```
|
||||
|
||||
在两个实例被创建和赋值后,下图表现了强引用的关系。变量`john`现在有一个指向`Person`实例的强引用,而变量`number73`有一个指向`Apartment`实例的强引用:
|
||||
在两个实例被创建和赋值后,下图表现了强引用的关系。变量`john`现在有一个指向`Person`实例的强引用,而变量`unit4A`有一个指向`Apartment`实例的强引用:
|
||||
|
||||

|
||||
|
||||
现在你能够将这两个实例关联在一起,这样人就能有公寓住了,而公寓也有了房客。注意感叹号是用来展开和访问可选变量`john`和`number73`中的实例,这样实例的属性才能被赋值:
|
||||
现在你能够将这两个实例关联在一起,这样人就能有公寓住了,而公寓也有了房客。注意感叹号是用来展开和访问可选变量`john`和`unit4A`中的实例,这样实例的属性才能被赋值:
|
||||
|
||||
```swift
|
||||
john!.apartment = number73
|
||||
number73!.tenant = john
|
||||
john!.apartment = unit4A
|
||||
unit4A!.tenant = john
|
||||
```
|
||||
|
||||
在将两个实例联系在一起之后,强引用的关系如图所示:
|
||||
|
||||

|
||||
|
||||
不幸的是,这两个实例关联后会产生一个循环强引用。`Person`实例现在有了一个指向`Apartment`实例的强引用,而`Apartment`实例也有了一个指向`Person`实例的强引用。因此,当你断开`john`和`number73`变量所持有的强引用时,引用计数并不会降为 0,实例也不会被 ARC 销毁:
|
||||
不幸的是,这两个实例关联后会产生一个循环强引用。`Person`实例现在有了一个指向`Apartment`实例的强引用,而`Apartment`实例也有了一个指向`Person`实例的强引用。因此,当你断开`john`和`unit4A`变量所持有的强引用时,引用计数并不会降为 0,实例也不会被 ARC 销毁:
|
||||
|
||||
```swift
|
||||
john = nil
|
||||
number73 = nil
|
||||
unit4A = nil
|
||||
```
|
||||
|
||||
注意,当你把这两个变量设为`nil`时,没有任何一个析构函数被调用。循环强引用会一直阻止`Person`和`Apartment`类实例的销毁,这就在你的应用程序中造成了内存泄漏。
|
||||
|
||||
在你将`john`和`number73`赋值为`nil`后,强引用关系如下图:
|
||||
在你将`john`和`unit4A`赋值为`nil`后,强引用关系如下图:
|
||||
|
||||

|
||||
|
||||
@ -213,24 +213,24 @@ class Person {
|
||||
|
||||
```swift
|
||||
class Apartment {
|
||||
let number: Int
|
||||
init(number: Int) { self.number = number }
|
||||
let unit: String
|
||||
init(unit: String) { self.unit = unit }
|
||||
weak var tenant: Person?
|
||||
deinit { print("Apartment #\(number) is being deinitialized") }
|
||||
deinit { print("Apartment \(unit) is being deinitialized") }
|
||||
}
|
||||
```
|
||||
|
||||
然后跟之前一样,建立两个变量(`john`和`number73`)之间的强引用,并关联两个实例:
|
||||
然后跟之前一样,建立两个变量(`john`和`unit4A`)之间的强引用,并关联两个实例:
|
||||
|
||||
```swift
|
||||
var john: Person?
|
||||
var number73: Apartment?
|
||||
var unit4A: Apartment?
|
||||
|
||||
john = Person(name: "John Appleseed")
|
||||
number73 = Apartment(number: 73)
|
||||
unit4A = Apartment(unit: "4A")
|
||||
|
||||
john!.apartment = number73
|
||||
number73!.tenant = john
|
||||
john!.apartment = unit4A
|
||||
unit4A!.tenant = john
|
||||
```
|
||||
|
||||
现在,两个关联在一起的实例的引用关系如下图所示:
|
||||
@ -248,18 +248,18 @@ john = nil
|
||||
// prints "John Appleseed is being deinitialized"
|
||||
```
|
||||
|
||||
唯一剩下的指向`Apartment`实例的强引用来自于变量`number73`。如果你断开这个强引用,再也没有指向`Apartment`实例的强引用了:
|
||||
唯一剩下的指向`Apartment`实例的强引用来自于变量`unit4A`。如果你断开这个强引用,再也没有指向`Apartment`实例的强引用了:
|
||||
|
||||

|
||||
|
||||
由于再也没有指向`Apartment`实例的强引用,该实例也会被销毁:
|
||||
|
||||
```swift
|
||||
number73 = nil
|
||||
// prints "Apartment #73 is being deinitialized"
|
||||
unit4A = nil
|
||||
// prints "Apartment 4A is being deinitialized"
|
||||
```
|
||||
|
||||
上面的两段代码展示了变量`john`和`number73`在被赋值为`nil`后,`Person`实例和`Apartment`实例的析构函数都打印出“销毁”的信息。这证明了引用循环被打破了。
|
||||
上面的两段代码展示了变量`john`和`unit4A`在被赋值为`nil`后,`Person`实例和`Apartment`实例的析构函数都打印出“销毁”的信息。这证明了引用循环被打破了。
|
||||
|
||||
<a name="2"></a>
|
||||
### 无主引用
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
错误处理是响应错误以及从错误中返回的过程。swift提供第一类错误支持,包括在运行时抛出,捕获,传送和控制可回收错误。
|
||||
|
||||
一些函数和方法不能总保证能够执行所有代码或产生有用的输出。可空类型用来表示值可能为空,但是当函数执行失败的事后,可空通常可以用来确定执行失败的原因,因此代码可以正确地响应失败。在Swift中,这叫做抛出函数或者抛出方法。
|
||||
一些函数和方法不能总保证能够执行所有代码或产生有用的输出。可空类型用来表示值可能为空,但是当函数执行失败的时候,可空通常可以用来确定执行失败的原因,因此代码可以正确地响应失败。在Swift中,这叫做抛出函数或者抛出方法。
|
||||
|
||||
举个例子,考虑到一个从磁盘上的一个文件读取以及处理数据的任务,有几种情况可能会导致这个任务失败,包括指定路径的文件不存在,文件不具有可读属性,或者文件没有被编码成合适的格式。区分这些错误可以让程序解决并且修复这些错误,并且,如果可能的话,把这些错误报告给用户。
|
||||
|
||||
@ -35,7 +35,7 @@ enum VendingMachineError: ErrorType {
|
||||
|
||||
<a name="throwing_errors"></a>
|
||||
错误抛出
|
||||
通过在函数或方法声明的参数后面加上`throws`关键字,表明这个函数或方法可以抛出错误。如果指定一个返回值,可以把`throws`关键字放在返回箭头(->)的前面。除非明确地指出,一个函数,方法或者就闭包不能抛出错误。
|
||||
通过在函数或方法声明的参数后面加上`throws`关键字,表明这个函数或方法可以抛出错误。如果指定一个返回值,可以把`throws`关键字放在返回箭头(->)的前面。除非明确地指出,一个函数,方法或者闭包就不能抛出错误。
|
||||
|
||||
```swift
|
||||
func canThrowErrors() throws -> String
|
||||
@ -174,4 +174,4 @@ func processFile(filename: String) throws {
|
||||
}
|
||||
```
|
||||
|
||||
上面这个例子使用了`defer`语句来保证`open`有对应的`close`。这个调用不管是否有抛出都会执行。
|
||||
上面这个例子使用了`defer`语句来保证`open`有对应的`close`。这个调用不管是否有抛出都会执行。
|
||||
|
||||
99
source/chapter2/19_Nested_Types.md
Normal file
99
source/chapter2/19_Nested_Types.md
Normal file
@ -0,0 +1,99 @@
|
||||
> 1.0
|
||||
> 翻译:[Lin-H](https://github.com/Lin-H)
|
||||
> 校对:[shinyzhu](https://github.com/shinyzhu)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[SergioChan](https://github.com/SergioChan)
|
||||
|
||||
# 嵌套类型
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [嵌套类型实例](#nested_types_in_action)
|
||||
- [嵌套类型的引用](#referring_to_nested_types)
|
||||
|
||||
枚举类型常被用于实现特定类或结构体的功能。也能够在有多种变量类型的环境中,方便地定义通用类或结构体来使用,为了实现这种功能,Swift允许你定义嵌套类型,可以在枚举类型、类和结构体中定义支持嵌套的类型。
|
||||
|
||||
要在一个类型中嵌套另一个类型,将需要嵌套的类型的定义写在被嵌套类型的区域{}内,而且可以根据需要定义多级嵌套。
|
||||
|
||||
<a name="nested_types_in_action"></a>
|
||||
##嵌套类型实例
|
||||
|
||||
下面这个例子定义了一个结构体`BlackjackCard`(二十一点),用来模拟`BlackjackCard`中的扑克牌点数。`BlackjackCard`结构体包含2个嵌套定义的枚举类型`Suit` 和 `Rank`。
|
||||
|
||||
在`BlackjackCard`规则中,`Ace`牌可以表示1或者11,`Ace`牌的这一特征用一个嵌套在枚举型`Rank`的结构体`Values`来表示。
|
||||
|
||||
```swift
|
||||
struct BlackjackCard {
|
||||
// 嵌套定义枚举型Suit
|
||||
enum Suit: Character {
|
||||
case Spades = "♠", Hearts = "♡", Diamonds = "♢", Clubs = "♣"
|
||||
}
|
||||
|
||||
// 嵌套定义枚举型Rank
|
||||
enum Rank: Int {
|
||||
case Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten
|
||||
case Jack, Queen, King, Ace
|
||||
struct Values {
|
||||
let first: Int, second: Int?
|
||||
}
|
||||
var values: Values {
|
||||
switch self {
|
||||
case .Ace:
|
||||
return Values(first: 1, second: 11)
|
||||
case .Jack, .Queen, .King:
|
||||
return Values(first: 10, second: nil)
|
||||
default:
|
||||
return Values(first: self.rawValue, second: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BlackjackCard 的属性和方法
|
||||
let rank: Rank, suit: Suit
|
||||
var description: String {
|
||||
var output = "suit is \(suit.rawValue),"
|
||||
output += " value is \(rank.values.first)"
|
||||
if let second = rank.values.second {
|
||||
output += " or \(second)"
|
||||
}
|
||||
return output
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
枚举型的`Suit`用来描述扑克牌的四种花色,并分别用一个`Character`类型的值代表花色符号。
|
||||
|
||||
枚举型的`Rank`用来描述扑克牌从`Ace`~10,`J`,`Q`,`K`,13张牌,并分别用一个`Int`类型的值表示牌的面值。(这个`Int`类型的值不适用于`Ace`,`J`,`Q`,`K`的牌)。
|
||||
|
||||
如上文所提到的,枚举型`Rank`在自己内部定义了一个嵌套结构体`Values`。在这个结构体中,只有`Ace`有两个数值,其余牌都只有一个数值。结构体`Values`中定义的两个属性:
|
||||
|
||||
`first`, 为` Int`
|
||||
`second`, 为 `Int?`, 或 “optional `Int`”
|
||||
|
||||
`Rank`定义了一个计算属性`values`,它将会返回一个结构体`Values`的实例。这个计算属性会根据牌的面值,用适当的数值去初始化`Values`实例,并赋值给`values`。对于`J`,`Q`,`K`,`Ace`会使用特殊数值,对于数字面值的牌使用`Int`类型的值。
|
||||
|
||||
`BlackjackCard`结构体自身有两个属性—`rank`与`suit`,也同样定义了一个计算属性`description`,`description`属性用`rank`和`suit`的中内容来构建对这张扑克牌名字和数值的描述,并用可选类型`second`来检查是否存在第二个值,若存在,则在原有的描述中增加对第二数值的描述。
|
||||
|
||||
因为`BlackjackCard`是一个没有自定义构造函数的结构体,在[结构体的逐一成员构造器](./14_Initialization.html#memberwise_initializers_for_structure_types)中知道结构体有默认的成员构造函数,所以你可以用默认的`initializer`去初始化新的常量`theAceOfSpades`:
|
||||
|
||||
```swift
|
||||
let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades)
|
||||
print("theAceOfSpades: \(theAceOfSpades.description)")
|
||||
// 打印出 "theAceOfSpades: suit is ♠, value is 1 or 11"
|
||||
```
|
||||
|
||||
尽管`Rank`和`Suit`嵌套在`BlackjackCard`中,但仍可被引用,所以在初始化实例时能够通过枚举类型中的成员名称单独引用。在上面的例子中`description`属性能正确得输出对`Ace`牌有1和11两个值。
|
||||
|
||||
<a name="referring_to_nested_types"></a>
|
||||
##嵌套类型的引用
|
||||
|
||||
在外部对嵌套类型的引用,以被嵌套类型的名字为前缀,加上所要引用的属性名:
|
||||
|
||||
```swift
|
||||
let heartsSymbol = BlackjackCard.Suit.Hearts.rawValue
|
||||
// 红心的符号 为 "♡"
|
||||
```
|
||||
|
||||
对于上面这个例子,这样可以使`Suit`, `Rank`, 和 `Values`的名字尽可能的短,因为它们的名字会自然的由定义它们的上下文来限定。
|
||||
@ -16,7 +16,7 @@
|
||||
- [`Any`和`AnyObject`的类型转换](#type_casting_for_any_and_anyobject)
|
||||
|
||||
|
||||
_类型转换_可以判断实例的类型,也可以将实例看做是其父类或者子类的实例。
|
||||
_类型转换_ 可以判断实例的类型,也可以将实例看做是其父类或者子类的实例。
|
||||
|
||||
类型转换在 Swift 中使用 `is` 和 `as` 操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。
|
||||
|
||||
|
||||
@ -69,8 +69,8 @@ struct BlackjackCard {
|
||||
|
||||
如上文所提到的,枚举型`Rank`在自己内部定义了一个嵌套结构体`Values`。在这个结构体中,只有`Ace`有两个数值,其余牌都只有一个数值。结构体`Values`中定义的两个属性:
|
||||
|
||||
`first`, 为` Int`
|
||||
`second`, 为 `Int?`, 或 “optional `Int`”
|
||||
- `first`为` Int`
|
||||
- `second`为 `Int?` 或 “optional `Int`”
|
||||
|
||||
`Rank`定义了一个计算属性`values`,它将会返回一个结构体`Values`的实例。这个计算属性会根据牌的面值,用适当的数值去初始化`Values`实例,并赋值给`values`。对于`J`,`Q`,`K`,`Ace`会使用特殊数值,对于数字面值的牌使用`Int`类型的值。
|
||||
|
||||
|
||||
261
source/chapter2/20_Type_Casting.md
Normal file
261
source/chapter2/20_Type_Casting.md
Normal file
@ -0,0 +1,261 @@
|
||||
> 1.0
|
||||
> 翻译:[xiehurricane](https://github.com/xiehurricane)
|
||||
> 校对:[happyming](https://github.com/happyming)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[yangsiy](https://github.com/yangsiy)
|
||||
|
||||
# 类型转换(Type Casting)
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [定义一个类层次作为例子](#defining_a_class_hierarchy_for_type_casting)
|
||||
- [检查类型](#checking_type)
|
||||
- [向下转型(Downcasting)](#downcasting)
|
||||
- [`Any`和`AnyObject`的类型转换](#type_casting_for_any_and_anyobject)
|
||||
|
||||
|
||||
_类型转换_可以判断实例的类型,也可以将实例看做是其父类或者子类的实例。
|
||||
|
||||
类型转换在 Swift 中使用 `is` 和 `as` 操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。
|
||||
|
||||
你也可以用它来检查一个类是否实现了某个协议,就像在 [检验协议的一致性](./22_Protocols.html#checking_for_protocol_conformance)部分讲述的一样。
|
||||
|
||||
<a name="defining_a_class_hierarchy_for_type_casting"></a>
|
||||
## 定义一个类层次作为例子
|
||||
|
||||
你可以将类型转换用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。下面的三个代码段定义了一个类层次和一个包含了几个这些类实例的数组,作为类型转换的例子。
|
||||
|
||||
第一个代码片段定义了一个新的基础类 `MediaItem`。这个类为任何出现在数字媒体库的媒体项提供基础功能。特别的,它声明了一个 `String` 类型的 `name` 属性,和一个 `init name` 初始化器。(假定所有的媒体项都有个名称。)
|
||||
|
||||
```swift
|
||||
class MediaItem {
|
||||
var name: String
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
下一个代码段定义了 `MediaItem` 的两个子类。第一个子类 `Movie` 封装了与电影相关的额外信息,在父类(或者说基类)的基础上增加了一个 `director`(导演)属性,和相应的初始化器。第二个子类 `Song`,在父类的基础上增加了一个 `artist`(艺术家)属性,和相应的初始化器:
|
||||
|
||||
```swift
|
||||
class Movie: MediaItem {
|
||||
var director: String
|
||||
init(name: String, director: String) {
|
||||
self.director = director
|
||||
super.init(name: name)
|
||||
}
|
||||
}
|
||||
|
||||
class Song: MediaItem {
|
||||
var artist: String
|
||||
init(name: String, artist: String) {
|
||||
self.artist = artist
|
||||
super.init(name: name)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
最后一个代码段创建了一个数组常量 `library`,包含两个 `Movie` 实例和三个 `Song` 实例。`library` 的类型是在它被初始化时根据它数组中所包含的内容推断来的。Swift的类型检测器能够推理出 `Movie` 和 `Song` 有共同的父类 `MediaItem`,所以它推断出 `[MediaItem]` 类作为 `library` 的类型。
|
||||
|
||||
```swift
|
||||
let library = [
|
||||
Movie(name: "Casablanca", director: "Michael Curtiz"),
|
||||
Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
|
||||
Movie(name: "Citizen Kane", director: "Orson Welles"),
|
||||
Song(name: "The One And Only", artist: "Chesney Hawkes"),
|
||||
Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
|
||||
]
|
||||
// the type of "library" is inferred to be [MediaItem]
|
||||
```
|
||||
|
||||
在幕后 `library` 里存储的媒体项依然是 `Movie` 和 `Song` 类型的。但是,若你迭代它,依次取出的实例会是 `MediaItem` 类型的,而不是 `Movie` 和 `Song` 类型。为了让它们作为原本的类型工作,你需要检查它们的类型或者向下转换它们到其它类型,就像下面描述的一样。
|
||||
|
||||
<a name="checking_type"></a>
|
||||
## 检查类型(Checking Type)
|
||||
|
||||
用类型检查操作符(`is`)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 `true`,否则返回 `false`。
|
||||
|
||||
下面的例子定义了两个变量,`movieCount` 和 `songCount`,用来计算数组 `library` 中 `Movie` 和 `Song` 类型的实例数量。
|
||||
|
||||
```swift
|
||||
var movieCount = 0
|
||||
var songCount = 0
|
||||
|
||||
for item in library {
|
||||
if item is Movie {
|
||||
++movieCount
|
||||
} else if item is Song {
|
||||
++songCount
|
||||
}
|
||||
}
|
||||
|
||||
print("Media library contains \(movieCount) movies and \(songCount) songs")
|
||||
// prints "Media library contains 2 movies and 3 songs"
|
||||
```
|
||||
|
||||
示例迭代了数组 `library` 中的所有项。每一次,`for`-`in` 循环设置
|
||||
`item` 为数组中的下一个 `MediaItem`。
|
||||
|
||||
若当前 `MediaItem` 是一个 `Movie` 类型的实例,`item is Movie` 返回
|
||||
`true`,相反返回 `false`。同样的,`item is
|
||||
Song` 检查item是否为 `Song` 类型的实例。在循环结束后,`movieCount` 和 `songCount` 的值就是被找到属于各自的类型的实例数量。
|
||||
|
||||
<a name="downcasting"></a>
|
||||
## 向下转型(Downcasting)
|
||||
|
||||
某类型的一个常量或变量可能在幕后实际上属于一个子类。当确定是这种情况时,你可以尝试向下转到它的子类型,用类型转换操作符(`as?` 或 `as!`)
|
||||
|
||||
因为向下转型可能会失败,类型转型操作符带有两种不同形式。条件形式(conditional form) `as?` 返回一个你试图向下转成的类型的可选值(optional value)。强制形式 `as!` 把试图向下转型和强制解包(force-unwraps)结果作为一个混合动作。
|
||||
|
||||
当你不确定向下转型可以成功时,用类型转换的条件形式(`as?`)。条件形式的类型转换总是返回一个可选值(optional value),并且若下转是不可能的,可选值将是 `nil`。这使你能够检查向下转型是否成功。
|
||||
|
||||
只有你可以确定向下转型一定会成功时,才使用强制形式(`as!`)。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。
|
||||
|
||||
下面的例子,迭代了 `library` 里的每一个 `MediaItem`,并打印出适当的描述。要这样做,`item` 需要真正作为 `Movie` 或 `Song` 的类型来使用,不仅仅是作为 `MediaItem`。为了能够在描述中使用 `Movie` 或 `Song` 的 `director` 或 `artist` 属性,这是必要的。
|
||||
|
||||
在这个示例中,数组中的每一个 `item` 可能是 `Movie` 或 `Song`。事前你不知道每个 `item` 的真实类型,所以这里使用条件形式的类型转换(`as?`)去检查循环里的每次下转。
|
||||
|
||||
```swift
|
||||
for item in library {
|
||||
if let movie = item as? Movie {
|
||||
print("Movie: '\(movie.name)', dir. \(movie.director)")
|
||||
} else if let song = item as? Song {
|
||||
print("Song: '\(song.name)', by \(song.artist)")
|
||||
}
|
||||
}
|
||||
|
||||
// Movie: 'Casablanca', dir. Michael Curtiz
|
||||
// Song: 'Blue Suede Shoes', by Elvis Presley
|
||||
// Movie: 'Citizen Kane', dir. Orson Welles
|
||||
// Song: 'The One And Only', by Chesney Hawkes
|
||||
// Song: 'Never Gonna Give You Up', by Rick Astley
|
||||
```
|
||||
|
||||
示例首先试图将 `item` 下转为 `Movie`。因为 `item` 是一个 `MediaItem`
|
||||
类型的实例,它可能是一个 `Movie`;同样,它也可能是一个 `Song`,或者仅仅是基类
|
||||
`MediaItem`。因为不确定,`as?`形式在试图下转时将返回一个可选值。`item as? Movie` 的返回值是 `Movie?` 或 “可选 `Movie`”类型。
|
||||
|
||||
当向下转型为 `Movie` 应用在两个 `Song`
|
||||
实例时将会失败。为了处理这种情况,上面的例子使用了可选绑定(optional binding)来检查可选 `Movie` 真的包含一个值(这个是为了判断下转是否成功。)可选绑定是这样写的“`if let movie = item as? Movie`”,可以这样解读:
|
||||
|
||||
“尝试将 `item` 转为 `Movie` 类型。若成功,设置一个新的临时常量 `movie` 来存储返回的可选 `Movie`”
|
||||
|
||||
若向下转型成功,然后 `movie` 的属性将用于打印一个 `Movie` 实例的描述,包括它的导演的名字 `director` 。相近的原理被用来检测 `Song` 实例,当 `Song` 被找到时则打印它的描述(包含 `artist` 的名字)。
|
||||
|
||||
> 注意:
|
||||
> 转换没有真的改变实例或它的值。潜在的根本的实例保持不变;只是简单地把它作为它被转换成的类来使用。
|
||||
|
||||
<a name="type_casting_for_any_and_anyobject"></a>
|
||||
## `Any`和`AnyObject`的类型转换
|
||||
|
||||
Swift为不确定类型提供了两种特殊类型别名:
|
||||
|
||||
* `AnyObject`可以代表任何class类型的实例。
|
||||
* `Any`可以表示任何类型,包括方法类型(function types)。
|
||||
|
||||
> 注意:
|
||||
> 只有当你明确的需要它的行为和功能时才使用`Any`和`AnyObject`。在你的代码里使用你期望的明确的类型总是更好的。
|
||||
|
||||
<a name="anyobject"></a>
|
||||
### `AnyObject`类型
|
||||
|
||||
当在工作中使用 Cocoa APIs,我们一般会接收一个`[AnyObject]`类型的数组,或者说“一个任何对象类型的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以从 API 提供的信息中清晰地确定数组中对象的类型。
|
||||
|
||||
在这些情况下,你可以使用强制形式的类型转换(`as`)来下转在数组中的每一项到比 `AnyObject` 更明确的类型,不需要可选解析(optional unwrapping)。
|
||||
|
||||
下面的示例定义了一个 `[AnyObject]` 类型的数组并填入三个`Movie`类型的实例:
|
||||
|
||||
```swift
|
||||
let someObjects: [AnyObject] = [
|
||||
Movie(name: "2001: A Space Odyssey", director: "Stanley Kubrick"),
|
||||
Movie(name: "Moon", director: "Duncan Jones"),
|
||||
Movie(name: "Alien", director: "Ridley Scott")
|
||||
]
|
||||
```
|
||||
|
||||
因为知道这个数组只包含 `Movie` 实例,你可以直接用(`as!`)下转并解包到不可选的`Movie`类型:
|
||||
|
||||
```swift
|
||||
for object in someObjects {
|
||||
let movie = object as! Movie
|
||||
print("Movie: '\(movie.name)', dir. \(movie.director)")
|
||||
}
|
||||
// Movie: '2001: A Space Odyssey', dir. Stanley Kubrick
|
||||
// Movie: 'Moon', dir. Duncan Jones
|
||||
// Movie: 'Alien', dir. Ridley Scott
|
||||
```
|
||||
|
||||
为了变为一个更短的形式,下转`someObjects`数组为`[Movie]`类型来代替下转数组中每一项的方式。
|
||||
|
||||
```swift
|
||||
for movie in someObjects as! [Movie] {
|
||||
print("Movie: '\(movie.name)', dir. \(movie.director)")
|
||||
}
|
||||
// Movie: '2001: A Space Odyssey', dir. Stanley Kubrick
|
||||
// Movie: 'Moon', dir. Duncan Jones
|
||||
// Movie: 'Alien', dir. Ridley Scott
|
||||
```
|
||||
|
||||
### `Any`类型
|
||||
|
||||
这里有个示例,使用 `Any` 类型来和混合的不同类型一起工作,包括方法类型和非 `class` 类型。它创建了一个可以存储`Any`类型的数组 `things`。
|
||||
|
||||
```swift
|
||||
var things = [Any]()
|
||||
|
||||
things.append(0)
|
||||
things.append(0.0)
|
||||
things.append(42)
|
||||
things.append(3.14159)
|
||||
things.append("hello")
|
||||
things.append((3.0, 5.0))
|
||||
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
|
||||
things.append({ (name: String) -> String in "Hello, \(name)" })
|
||||
```
|
||||
|
||||
`things` 数组包含两个 `Int` 值,2个 `Double` 值,1个 `String` 值,一个元组 `(Double, Double)` ,电影“Ghostbusters”,和一个获取 `String` 值并返回另一个 `String` 值的闭包表达式。
|
||||
|
||||
你可以在 `switch` 表达式的cases中使用 `is` 和 `as` 操作符来发觉只知道是 `Any` 或 `AnyObject` 的常量或变量的类型。下面的示例迭代 `things` 数组中的每一项的并用`switch`语句查找每一项的类型。这几种 `switch` 语句的情形绑定它们匹配的值到一个规定类型的常量,让它们的值可以被打印:
|
||||
|
||||
```swift
|
||||
for thing in things {
|
||||
switch thing {
|
||||
case 0 as Int:
|
||||
print("zero as an Int")
|
||||
case 0 as Double:
|
||||
print("zero as a Double")
|
||||
case let someInt as Int:
|
||||
print("an integer value of \(someInt)")
|
||||
case let someDouble as Double where someDouble > 0:
|
||||
print("a positive double value of \(someDouble)")
|
||||
case is Double:
|
||||
print("some other double value that I don't want to print")
|
||||
case let someString as String:
|
||||
print("a string value of \"\(someString)\"")
|
||||
case let (x, y) as (Double, Double):
|
||||
print("an (x, y) point at \(x), \(y)")
|
||||
case let movie as Movie:
|
||||
print("a movie called '\(movie.name)', dir. \(movie.director)")
|
||||
case let stringConverter as String -> String:
|
||||
print(stringConverter("Michael"))
|
||||
default:
|
||||
print("something else")
|
||||
}
|
||||
}
|
||||
|
||||
// zero as an Int
|
||||
// zero as a Double
|
||||
// an integer value of 42
|
||||
// a positive double value of 3.14159
|
||||
// a string value of "hello"
|
||||
// an (x, y) point at 3.0, 5.0
|
||||
// a movie called 'Ghostbusters', dir. Ivan Reitman
|
||||
// Hello, Michael
|
||||
```
|
||||
|
||||
|
||||
> 注意:
|
||||
> 在一个switch语句的case中使用强制形式的类型转换操作符(as, 而不是 as?)来检查和转换到一个明确的类型。在 `switch` case 语句的内容中这种检查总是安全的。
|
||||
@ -28,8 +28,7 @@ Swift 中的扩展可以:
|
||||
- 定义和使用新的嵌套类型
|
||||
- 使一个已有类型符合某个协议
|
||||
|
||||
TODO:
|
||||
在 Swift 中,你甚至可以对一个协议(Procotol)进行扩展,提供协议需要的实现,或者添加额外的功能能够对合适的类型带来额外的好处。你可以从[协议扩展](./22_Protocols.html#protocol_extensions)获取更多的细节。
|
||||
在 Swift 中,你甚至可以对一个协议(Protocol)进行扩展,提供协议需要的实现,或者添加额外的功能能够对合适的类型带来额外的好处。你可以从[协议扩展](./22_Protocols.html#protocol_extensions)获取更多的细节。
|
||||
|
||||
>注意:
|
||||
扩展可以对一个类型添加新的功能,但是不能重写已有的功能。
|
||||
|
||||
Reference in New Issue
Block a user