From 84da8738465d4e7e4fe80c1f3299464902abf129 Mon Sep 17 00:00:00 2001 From: dzy_PC Date: Thu, 25 Oct 2018 15:01:55 +0800 Subject: [PATCH 1/8] =?UTF-8?q?4.2=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/20_Extensions.md | 248 +++++++++++++++---------------- 1 file changed, 117 insertions(+), 131 deletions(-) diff --git a/source/chapter2/20_Extensions.md b/source/chapter2/20_Extensions.md index 51b25f36..8468813f 100644 --- a/source/chapter2/20_Extensions.md +++ b/source/chapter2/20_Extensions.md @@ -1,101 +1,96 @@ # 扩展 -*扩展* 就是为一个已有的类、结构体、枚举类型或者协议类型添加新功能。这包括在没有权限获取原始源代码的情况下扩展类型的能力(即 *逆向建模* )。扩展和 Objective-C 中的分类类似。(与 Objective-C 不同的是,Swift 的扩展没有名字。) +*扩展*可以给一个现有的类,结构体,枚举,还有协议添加新的功能。它还拥有,不需要访问被扩展类型源代码就能完成扩展的能力(即*逆向建模*)。扩展和 Objective-C 的分类很相似。(与 Objective-C 分类不同的是,Swift 扩展是没有名字的。) -Swift 中的扩展可以: +Swift 中的扩展可以: -- 添加计算型属性和计算型类型属性 -- 定义实例方法和类型方法 -- 提供新的构造器 -- 定义下标 -- 定义和使用新的嵌套类型 -- 使一个已有类型符合某个协议 + - 添加计算型实例属性和计算型类属性 + - 定义实例方法和类方法 + - 提供新的构造器 + - 定义下标 + - 定义和使用新的嵌套类型 + - 使已经存在的类型 conform(遵循)一个协议 -在 Swift 中,你甚至可以对协议进行扩展,提供协议要求的实现,或者添加额外的功能,从而可以让符合协议的类型拥有这些功能。你可以从[协议扩展](./21_Protocols.html#protocol_extensions)获取更多的细节。 +在Swift中,你甚至可以扩展协议以提供其需要的实现,或者添加额外功能给遵循的类型所使用。你可以从[协议扩展](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID521)获取更多细节。 -> 注意 +> 注意 > -> 扩展可以为一个类型添加新的功能,但是不能重写已有的功能。 +> 扩展可以给一个类型添加新的功能,但是不能重写已经存在的功能。 - -## 扩展语法 +## 扩展的语法 +使用 **extension** 关键字声明扩展: -使用关键字 `extension` 来声明扩展: - -```swift +``` extension SomeType { - // 为 SomeType 添加的新功能写到这里 + // 在这里给 SomeType 添加新的功能 } +``` + +扩展可以扩充一个现有的类型,给它添加一个或多个协议。协议名称的写法和类或者结构体一样: + ``` - -可以通过扩展来扩展一个已有类型,使其采纳一个或多个协议。在这种情况下,无论是类还是结构体,协议名字的书写方式完全一样: - -```swift -extension SomeType: SomeProtocol, AnotherProctocol { - // 协议实现写到这里 +extension SomeType: SomeProtocol, AnotherProtocol { + // 协议所需要的实现写在这里 } -``` +``` -通过这种方式添加协议一致性的详细描述请参阅[利用扩展添加协议一致性](./21_Protocols.html#adding_protocol_conformance_with_an_extension)。 +通过这种方式遵循协议在[使用扩展遵循协议](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID277)中有描述。 -> 注意 +扩展可以使用在现有范型类型上,就像[扩展范型类型](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID185)中描述的一样。你还可以使用扩展给泛型类型有条件的添加功能,就像[扩展一个带有 Where 字句的范型](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID553)中描述的一样。 + +> 注意 > -> 如果你通过扩展为一个已有类型添加新功能,那么新功能对该类型的所有已有实例都是可用的,即使它们是在这个扩展定义之前创建的。 +> 对一个现有的类型,如果你定义了一个扩展来添加新的功能,那么这个类型的所有实例都可以使用这个新功能,甚至是定义扩展之前就被创建的。 - ## 计算型属性 +扩展可以给现有类型添加计算型实例属性和计算型类属性。这个例子给 Swift 内建的 **Double** 类型添加了五个计算型实例属性,从而提供与距离单位相关工作的基本支持: -扩展可以为已有类型添加计算型实例属性和计算型类型属性。下面的例子为 Swift 的内建 `Double` 类型添加了五个计算型实例属性,从而提供与距离单位协作的基本支持: - -```swift +``` extension Double { var km: Double { return self * 1_000.0 } - var m : Double { return self } + var m: Double { return self } var cm: Double { return self / 100.0 } var mm: Double { return self / 1_000.0 } var ft: Double { return self / 3.28084 } } let oneInch = 25.4.mm print("One inch is \(oneInch) meters") -// 打印 “One inch is 0.0254 meters” +// 打印“一英寸是 0.0254 米” let threeFeet = 3.ft print("Three feet is \(threeFeet) meters") -// 打印 “Three feet is 0.914399970739201 meters” +// 打印“三英尺是 0.914399970739201 米” +``` + +这些计算型属性表示应该将 **Double** 类型的值看作一个长度单位。即使它们被实现为计算型属性,但是这些属性的名称可以通过点语法附加到浮点小数上,作为一种方式来使用文字执行距离转换。 + +在上述例子中,**Double** 类型的 **1.0** 代表的是“一米”。这就是为什么计算型属性 **m** 返回的是 **self** - 表达式 **1.m** 被认为是计算一个 **Double** 类型的 **1.0**。 + +其他单位使用米表示的时候需要进行一些转换。One kilometer(一千米)等于 1,000 meters(米),所以 **km** 计算型属性将值乘以 **1_000.00** 转换成单位为米的值。类似的,一米等于 3.28084 英尺,所以 **ft** 计算型属性将给予的 **Double** 值除以 **3.28084**,用以将英尺转换成米。 + +这些属性都是只读的计算型属性,所以为了简便,他们的表达式里面都不包含 **get** 关键字。它们使用 **Double** 作为返回值类型,并可用于所有接受 **Double** 类型的数学计算中: + ``` - -这些计算型属性表达的含义是把一个 `Double` 值看作是某单位下的长度值。即使它们被实现为计算型属性,但这些属性的名字仍可紧接一个浮点型字面值,从而通过点语法来使用,并以此实现距离转换。 - -在上述例子中,`Double` 值 `1.0` 用来表示“1米”。这就是为什么计算型属性 `m` 返回 `self`,即表达式 `1.m` 被认为是计算 `Double` 值 `1.0`。 - -其它单位则需要一些单位换算。一千米等于 1,000 米,所以计算型属性 `km` 要把值乘以 `1_000.00` 来实现千米到米的单位换算。类似地,一米有 3.28024 英尺,所以计算型属性 `ft` 要把对应的 `Double` 值除以 `3.28024` 来实现英尺到米的单位换算。 - -这些属性是只读的计算型属性,为了更简洁,省略了 `get` 关键字。它们的返回值是 `Double`,而且可以用于所有接受 `Double` 值的数学计算中: - -```swift let aMarathon = 42.km + 195.m print("A marathon is \(aMarathon) meters long") -// 打印 “A marathon is 42195.0 meters long” -``` +// 打印“马拉松赛跑全长 42195.0 米。” +``` -> 注意 -> -> 扩展可以添加新的计算型属性,但是不可以添加存储型属性,也不可以为已有属性添加属性观察器。 +> 注意 +> +> 扩展可以添加新的计算属性,但是它们不能添加存储属性,或向现有的属性添加属性观察者。 - ## 构造器 +扩展可以给现有的类型添加新的构造器。它使你可以把自定义类型作为参数来供其他类型的构造器使用,或者在类型的原始实现上添加额外的构造选项。 -扩展可以为已有类型添加新的构造器。这可以让你扩展其它类型,将你自己的定制类型作为其构造器参数,或者提供该类型的原始实现中未提供的额外初始化选项。 +扩展可以给一个类添加新的便利构造器,但是它们不能给类添加新的指定构造器或者析构器。指定构造器和析构器必须始终由类的原始实现提供。 -扩展能为类添加新的便利构造器,但是它们不能为类添加新的指定构造器或析构器。指定构造器和析构器必须总是由原始的类实现来提供。 +如果你使用扩展给一个值类型添加构造器只是用于给所有的存储属性提供默认值,并且没有定义任何自定义构造器,那么你可以在该值类型扩展的构造器中使用默认构造器和成员构造器。如果你把构造器写到了值类型的原始实现中,就像[值类型的构造器委托](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID215)中描述的这样,那么就不是上面我们所提到的情况。 -> 注意 -> -> 如果你使用扩展为一个值类型添加构造器,同时该值类型的原始实现中未定义任何定制的构造器且所有存储属性提供了默认值,那么我们就可以在扩展中的构造器里调用默认构造器和逐一成员构造器。 -正如在[值类型的构造器代理](./14_Initialization.html#initializer_delegation_for_value_types)中描述的,如果你把定制的构造器写在值类型的原始实现中,上述规则将不再适用。 +如果你使用扩展给另一个模块中定义的结构体添加构造器,那么新的构造器直到定义模块中使用一个构造器之前,不能访问 **self**。 -下面的例子定义了一个用于描述几何矩形的结构体 `Rect`。这个例子同时定义了两个辅助结构体 `Size` 和 `Point`,它们都把 `0.0` 作为所有属性的默认值: +在下面的例子中,自定义了一个的 **Rect** 结构体用来表示一个几何矩形。这个例子中还定义了两个给予支持的结构体 **Size** 和 **Point**,它们都把属性的默认值设置为 **0.0**: -```swift +``` struct Size { var width = 0.0, height = 0.0 } @@ -106,19 +101,19 @@ struct Rect { var origin = Point() var size = Size() } +``` + +因为 **Rect** 结构体给所有的属性都提供了默认值,所以它自动获得了一个默认构造器和一个成员构造器,就像[默认构造器](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID213)中描述的一样。这些构造器可以用来创建新的 **Rect** 实例: + ``` - -因为结构体 `Rect` 未提供定制的构造器,因此它会获得一个逐一成员构造器。又因为它为所有存储型属性提供了默认值,它又会获得一个默认构造器。详情请参阅[默认构造器](./14_Initialization.html#default_initializers)。这些构造器可以用于构造新的 `Rect` 实例: - -```swift let defaultRect = Rect() let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), - size: Size(width: 5.0, height: 5.0)) + size: Size(width: 5.0, height: 5.0)) +``` + +你可以通过扩展 **Rect** 结构体来提供一个允许指定 point 和 size 的构造器: + ``` - -你可以提供一个额外的接受指定中心点和大小的构造器来扩展 `Rect` 结构体: - -```swift extension Rect { init(center: Point, size: Size) { let originX = center.x - (size.width / 2) @@ -128,24 +123,22 @@ extension Rect { } ``` -这个新的构造器首先根据提供的 `center` 和 `size` 的值计算一个合适的原点。然后调用该结构体的逐一成员构造器 `init(origin:size:)`,该构造器将新的原点和大小的值保存到了相应的属性中: +这个新的构造器首先根据提供的 **center** 和 **size** 计算一个适当的原点。然后这个构造器调用结构体自带的成员构造器 **init(origin:size:)**,它会将新的 origin 和 size 值储存在适当的属性中: -```swift -let centerRect = Rect(center: Point(x: 4.0, y: 4.0), - size: Size(width: 3.0, height: 3.0)) -// centerRect 的原点是 (2.5, 2.5),大小是 (3.0, 3.0) ``` +let centerRect = Rect(center: Point(x: 4.0, y: 4.0), + size: Size(width: 3.0, height: 3.0)) +// centerRect 的 origin 是 (2.5, 2.5) 并且它的 size 是 (3.0, 3.0) +``` -> 注意 +> 注意 > -> 如果你使用扩展提供了一个新的构造器,你依旧有责任确保构造过程能够让实例完全初始化。 +> 如果你通过扩展提供一个新的构造器,你有责任确保每个通过该构造器创建的实例都是初始化完整的。 - ## 方法 +扩展可以给现有类型添加新的实例方法和类方法。在下面的例子中,给 **Int** 类型添加了一个新的实例方法叫做 **repetitions**: -扩展可以为已有类型添加新的实例方法和类型方法。下面的例子为 `Int` 类型添加了一个名为 `repetitions` 的实例方法: - -```swift +``` extension Int { func repetitions(task: () -> Void) { for _ in 0.. Void` 类型的单参数,表示没有参数且没有返回值的函数。 +**repetitions(task:)** 方法仅接收一个 **() -> Void** 类型的参数,它表示一个没有参数没有返回值的方法。 -定义该扩展之后,你就可以对任意整数调用 `repetitions(task:)` 方法,将闭包中的任务执行整数对应的次数: - -```swift -3.repetitions({ - print("Hello!") -}) -// Hello! -// Hello! -// Hello! +定义了这个扩展之后,你可以对任意整形数值调用 **repetitions(task:)** 方法,来执行对应次数的任务: ``` +3.repetitions { + print("Hello!") +} +// Hello! +// Hello! +// Hello! +``` - - ### 可变实例方法 +通过扩展添加的实例方法同样也可以修改(或 *mutating(改变)*)实例本身。结构体和枚举的方法,若是可以修改 **self** 或者它自己的属性,则必须将这个实例方法标记为 **mutating**,就像是改变了方法的原始实现。 -通过扩展添加的实例方法也可以修改该实例本身。结构体和枚举类型中修改 `self` 或其属性的方法必须将该实例方法标注为 `mutating`,正如来自原始实现的可变方法一样。 +在下面的例子中,对 Swift 的 **Int** 类型添加了一个新的 mutating 方法,叫做 **square**,它将原始值求平方: -下面的例子为 Swift 的 `Int` 类型添加了一个名为 `square` 的可变方法,用于计算原始值的平方值: - -```swift +``` extension Int { mutating func square() { self = self * self @@ -184,20 +173,17 @@ extension Int { } var someInt = 3 someInt.square() -// someInt 的值现在是 9 -``` +// someInt 现在是 9 +``` - ## 下标 +扩展可以给现有的类型添加新的下标。下面的例子中,对 Swift 的 **Int** 类型添加了一个整数类型的下标。下标 **[n]** 从数字右侧开始,返回小数点后的第 **n** 位: -扩展可以为已有类型添加新下标。这个例子为 Swift 内建类型 `Int` 添加了一个整型下标。该下标 `[n]` 返回十进制数字从右向左数的第 `n` 个数字: +- **123456789[0]** returns **9** +- **123456789[1]** returns **8** -- `123456789[0]` 返回 `9` -- `123456789[1]` 返回 `8` - -……以此类推。 - -```swift +……以此类推: +``` extension Int { subscript(digitIndex: Int) -> Int { var decimalBase = 1 @@ -208,29 +194,27 @@ extension Int { } } 746381295[0] -// 返回 5 +// returns 5 746381295[1] -// 返回 9 +// returns 9 746381295[2] -// 返回 2 +// returns 2 746381295[8] -// 返回 7 +// returns 7 +``` + +如果操作的 **Int** 值没有足够的位数满足所请求的下标,那么下标的现实将返回 **0**,将好像在数字的左边补上了 0: + ``` - -如果该 `Int` 值没有足够的位数,即下标越界,那么上述下标实现会返回 `0`,犹如在数字左边自动补 `0`: - -```swift 746381295[9] -// 返回 0,即等同于: +// 返回 0,就好像你进行了这个请求: 0746381295[9] -``` +``` - ## 嵌套类型 +扩展可以给现有的类,结构体,还有枚举添加新的嵌套类型: -扩展可以为已有的类、结构体和枚举添加新的嵌套类型: - -```swift +``` extension Int { enum Kind { case negative, zero, positive @@ -246,15 +230,15 @@ extension Int { } } } +``` + +这个例子给 **Int** 添加了一个新的嵌套枚举。这个枚举叫做 **Kind**,表示特定整数所代表的数字类型。具体来说,它表示数字是负的、零的还是正的。 + +这个例子同样给 **Int** 添加了一个新的计算型实例属性,叫做 **kind**,它返回被操作整数所对应的 **Kind** 枚举 case 分支。 + +现在,任意 **Int** 的值都可以使用这个嵌套类型: + ``` - -该例子为 `Int` 添加了嵌套枚举。这个名为 `Kind` 的枚举表示特定整数的类型。具体来说,就是表示整数是正数、零或者负数。 - -这个例子还为 `Int` 添加了一个计算型实例属性,即 `kind`,用来根据整数返回适当的 `Kind` 枚举成员。 - -现在,这个嵌套枚举可以和任意 `Int` 值一起使用了: - -```swift func printIntegerKinds(_ numbers: [Int]) { for number in numbers { switch number.kind { @@ -269,11 +253,13 @@ func printIntegerKinds(_ numbers: [Int]) { print("") } printIntegerKinds([3, 19, -27, 0, -6, 0, 7]) -// 打印 “+ + - 0 - 0 + ” +// Prints "+ + - 0 - 0 + " ``` -函数 `printIntegerKinds(_:)` 接受一个 `Int` 数组,然后对该数组进行迭代。在每次迭代过程中,对当前整数的计算型属性 `kind` 的值进行评估,并打印出适当的描述。 +方法 **printIntegerKinds(_:)**,使用一个 **Int** 类型的数组作为输入,然后依次迭代这些值。对于数组中的每一个整数,方法会检查它的 **kind** 计算型属性,然后打印适当的描述。 + +> 注意 +> +> **number.kind** 已经被认为是 **Int.Kind** 类型。所以,在 **switch** 语句中所有的 **Int.Kind** case 分支可以被缩写,就像使用 **.negative** 替代 **Int.Kind.negative.**。 + -> 注意 -> -> 由于已知 `number.kind` 是 `Int.Kind` 类型,因此在 `switch` 语句中,`Int.Kind` 中的所有成员值都可以使用简写形式,例如使用 `.negative` 而不是 `Int.Kind.negative`。 From 1b7b7ed1abbb006c7ace3df74725aa1f47d0acaf Mon Sep 17 00:00:00 2001 From: dzy_PC Date: Wed, 7 Nov 2018 09:59:00 +0800 Subject: [PATCH 2/8] =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E4=B9=8B=E5=90=8E?= =?UTF-8?q?=E7=9A=84=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/20_Extensions.md | 59 ++++++++++++++++---------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/source/chapter2/20_Extensions.md b/source/chapter2/20_Extensions.md index 8468813f..5a8ca127 100644 --- a/source/chapter2/20_Extensions.md +++ b/source/chapter2/20_Extensions.md @@ -1,6 +1,6 @@ # 扩展 -*扩展*可以给一个现有的类,结构体,枚举,还有协议添加新的功能。它还拥有,不需要访问被扩展类型源代码就能完成扩展的能力(即*逆向建模*)。扩展和 Objective-C 的分类很相似。(与 Objective-C 分类不同的是,Swift 扩展是没有名字的。) +*扩展*可以给一个现有的类,结构体,枚举,还有协议添加新的功能。它还拥有不需要访问被扩展类型源代码就能完成扩展的能力(即*逆向建模*)。扩展和 Objective-C 的分类很相似。(与 Objective-C 分类不同的是,Swift 扩展是没有名字的。) Swift 中的扩展可以: @@ -9,9 +9,9 @@ Swift 中的扩展可以: - 提供新的构造器 - 定义下标 - 定义和使用新的嵌套类型 - - 使已经存在的类型 conform(遵循)一个协议 + - 使已经存在的类型遵循(conform)一个协议 -在Swift中,你甚至可以扩展协议以提供其需要的实现,或者添加额外功能给遵循的类型所使用。你可以从[协议扩展](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID521)获取更多细节。 +在 Swift 中,你甚至可以扩展协议以提供其需要的实现,或者添加额外功能给遵循的类型所使用。你可以从 [协议扩展](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID521) 获取更多细节。 > 注意 > @@ -20,32 +20,32 @@ Swift 中的扩展可以: ## 扩展的语法 使用 **extension** 关键字声明扩展: -``` +```swift extension SomeType { - // 在这里给 SomeType 添加新的功能 + // 在这里给 SomeType 添加新的功能 } ``` 扩展可以扩充一个现有的类型,给它添加一个或多个协议。协议名称的写法和类或者结构体一样: -``` +```swift extension SomeType: SomeProtocol, AnotherProtocol { - // 协议所需要的实现写在这里 + // 协议所需要的实现写在这里 } ``` -通过这种方式遵循协议在[使用扩展遵循协议](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID277)中有描述。 +这种遵循协议的方式在 [使用扩展遵循协议](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID277) 中有描述。 -扩展可以使用在现有范型类型上,就像[扩展范型类型](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID185)中描述的一样。你还可以使用扩展给泛型类型有条件的添加功能,就像[扩展一个带有 Where 字句的范型](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID553)中描述的一样。 +扩展可以使用在现有范型类型上,就像 [扩展范型类型](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID185) 中描述的一样。你还可以使用扩展给泛型类型有条件的添加功能,就像 [扩展一个带有 Where 字句的范型](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID553) 中描述的一样。 > 注意 > -> 对一个现有的类型,如果你定义了一个扩展来添加新的功能,那么这个类型的所有实例都可以使用这个新功能,甚至是定义扩展之前就被创建的。 +> 对一个现有的类型,如果你定义了一个扩展来添加新的功能,那么这个类型的所有实例都可以使用这个新功能,包括那些在扩展定义之前就存在的实例。 ## 计算型属性 扩展可以给现有类型添加计算型实例属性和计算型类属性。这个例子给 Swift 内建的 **Double** 类型添加了五个计算型实例属性,从而提供与距离单位相关工作的基本支持: -``` +```swift extension Double { var km: Double { return self * 1_000.0 } var m: Double { return self } @@ -61,15 +61,15 @@ print("Three feet is \(threeFeet) meters") // 打印“三英尺是 0.914399970739201 米” ``` -这些计算型属性表示应该将 **Double** 类型的值看作一个长度单位。即使它们被实现为计算型属性,但是这些属性的名称可以通过点语法附加到浮点小数上,作为一种方式来使用文字执行距离转换。 +这些计算型属性表示的含义是把一个 **Double** 值看作是某单位下的长度值。即使它们被实现为计算型属性,但这些属性的名字仍可紧接一个浮点型字面值,从而通过点语法来使用,并以此实现距离转换。 在上述例子中,**Double** 类型的 **1.0** 代表的是“一米”。这就是为什么计算型属性 **m** 返回的是 **self** - 表达式 **1.m** 被认为是计算一个 **Double** 类型的 **1.0**。 -其他单位使用米表示的时候需要进行一些转换。One kilometer(一千米)等于 1,000 meters(米),所以 **km** 计算型属性将值乘以 **1_000.00** 转换成单位为米的值。类似的,一米等于 3.28084 英尺,所以 **ft** 计算型属性将给予的 **Double** 值除以 **3.28084**,用以将英尺转换成米。 +其它单位则需要一些单位换算。一千米等于 1,000 米,所以计算型属性 **km** 要把值乘以 **1_000.00** 来实现千米到米的单位换算。类似地,一米有 3.28084 英尺,所以计算型属性 **ft** 要把对应的 **Double** 值除以 **3.28084**,来实现英尺到米的单位换算。 -这些属性都是只读的计算型属性,所以为了简便,他们的表达式里面都不包含 **get** 关键字。它们使用 **Double** 作为返回值类型,并可用于所有接受 **Double** 类型的数学计算中: +这些属性都是只读的计算型属性,所以为了简便,它们的表达式里面都不包含 **get** 关键字。它们使用 **Double** 作为返回值类型,并可用于所有接受 **Double** 类型的数学计算中: -``` +```swift let aMarathon = 42.km + 195.m print("A marathon is \(aMarathon) meters long") // 打印“马拉松赛跑全长 42195.0 米。” @@ -84,13 +84,13 @@ print("A marathon is \(aMarathon) meters long") 扩展可以给一个类添加新的便利构造器,但是它们不能给类添加新的指定构造器或者析构器。指定构造器和析构器必须始终由类的原始实现提供。 -如果你使用扩展给一个值类型添加构造器只是用于给所有的存储属性提供默认值,并且没有定义任何自定义构造器,那么你可以在该值类型扩展的构造器中使用默认构造器和成员构造器。如果你把构造器写到了值类型的原始实现中,就像[值类型的构造器委托](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID215)中描述的这样,那么就不是上面我们所提到的情况。 +如果你使用扩展给一个值类型添加构造器只是用于给所有的存储属性提供默认值,并且没有定义任何自定义构造器,那么你可以在该值类型扩展的构造器中使用默认构造器和成员构造器。如果你把构造器写到了值类型的原始实现中,就像 [值类型的构造器委托](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID215) 中所描述的,那么就不属于在扩展中添加构造器。 如果你使用扩展给另一个模块中定义的结构体添加构造器,那么新的构造器直到定义模块中使用一个构造器之前,不能访问 **self**。 在下面的例子中,自定义了一个的 **Rect** 结构体用来表示一个几何矩形。这个例子中还定义了两个给予支持的结构体 **Size** 和 **Point**,它们都把属性的默认值设置为 **0.0**: -``` +```swift struct Size { var width = 0.0, height = 0.0 } @@ -103,9 +103,9 @@ struct Rect { } ``` -因为 **Rect** 结构体给所有的属性都提供了默认值,所以它自动获得了一个默认构造器和一个成员构造器,就像[默认构造器](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID213)中描述的一样。这些构造器可以用来创建新的 **Rect** 实例: +因为 **Rect** 结构体给所有的属性都提供了默认值,所以它自动获得了一个默认构造器和一个成员构造器,就像 [默认构造器](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID213) 中描述的一样。这些构造器可以用来创建新的 **Rect** 实例: -``` +```swift let defaultRect = Rect() let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0)) @@ -113,7 +113,7 @@ let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), 你可以通过扩展 **Rect** 结构体来提供一个允许指定 point 和 size 的构造器: -``` +```swift extension Rect { init(center: Point, size: Size) { let originX = center.x - (size.width / 2) @@ -125,7 +125,7 @@ extension Rect { 这个新的构造器首先根据提供的 **center** 和 **size** 计算一个适当的原点。然后这个构造器调用结构体自带的成员构造器 **init(origin:size:)**,它会将新的 origin 和 size 值储存在适当的属性中: -``` +```swift let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0)) // centerRect 的 origin 是 (2.5, 2.5) 并且它的 size 是 (3.0, 3.0) @@ -138,7 +138,7 @@ let centerRect = Rect(center: Point(x: 4.0, y: 4.0), ## 方法 扩展可以给现有类型添加新的实例方法和类方法。在下面的例子中,给 **Int** 类型添加了一个新的实例方法叫做 **repetitions**: -``` +```swift extension Int { func repetitions(task: () -> Void) { for _ in 0.. Void** 类型的参数,它表示一个没有参数没有返回值的方法。 定义了这个扩展之后,你可以对任意整形数值调用 **repetitions(task:)** 方法,来执行对应次数的任务: -``` + +```swift 3.repetitions { print("Hello!") } @@ -165,7 +166,7 @@ extension Int { 在下面的例子中,对 Swift 的 **Int** 类型添加了一个新的 mutating 方法,叫做 **square**,它将原始值求平方: -``` +```swift extension Int { mutating func square() { self = self * self @@ -183,7 +184,7 @@ someInt.square() - **123456789[1]** returns **8** ……以此类推: -``` +```swift extension Int { subscript(digitIndex: Int) -> Int { var decimalBase = 1 @@ -205,7 +206,7 @@ extension Int { 如果操作的 **Int** 值没有足够的位数满足所请求的下标,那么下标的现实将返回 **0**,将好像在数字的左边补上了 0: -``` +```swift 746381295[9] // 返回 0,就好像你进行了这个请求: 0746381295[9] @@ -214,7 +215,7 @@ extension Int { ## 嵌套类型 扩展可以给现有的类,结构体,还有枚举添加新的嵌套类型: -``` +```swift extension Int { enum Kind { case negative, zero, positive @@ -238,7 +239,7 @@ extension Int { 现在,任意 **Int** 的值都可以使用这个嵌套类型: -``` +```swift func printIntegerKinds(_ numbers: [Int]) { for number in numbers { switch number.kind { @@ -253,7 +254,7 @@ func printIntegerKinds(_ numbers: [Int]) { print("") } printIntegerKinds([3, 19, -27, 0, -6, 0, 7]) -// Prints "+ + - 0 - 0 + " +// 打印 "+ + - 0 - 0 + " ``` 方法 **printIntegerKinds(_:)**,使用一个 **Int** 类型的数组作为输入,然后依次迭代这些值。对于数组中的每一个整数,方法会检查它的 **kind** 计算型属性,然后打印适当的描述。 From 75495c45c830b602dc3b310d186bf965bfbb3643 Mon Sep 17 00:00:00 2001 From: dzy_PC Date: Mon, 31 Dec 2018 14:10:39 +0800 Subject: [PATCH 3/8] =?UTF-8?q?4.2=20=E5=AF=B9=E5=BA=94=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E7=BB=B4=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/26_Advanced_Operators.md | 190 +++++++++++++---------- 1 file changed, 104 insertions(+), 86 deletions(-) diff --git a/source/chapter2/26_Advanced_Operators.md b/source/chapter2/26_Advanced_Operators.md index 3356dcaf..e5aedd73 100644 --- a/source/chapter2/26_Advanced_Operators.md +++ b/source/chapter2/26_Advanced_Operators.md @@ -1,12 +1,12 @@ # 高级运算符 -除了在之前介绍过的[基本运算符](./02_Basic_Operators.html),Swift 中还有许多可以对数值进行复杂运算的高级运算符。这些高级运算符包含了在 C 和 Objective-C 中已经被大家所熟知的位运算符和移位运算符。 +除了之前介绍过的[基本运算符](./02_Basic_Operators.md),Swift 还提供了数种可以对数值进行复杂运算的高级运算符。它们包含了在 C 和 Objective-C 中已经被大家所熟知的位运算符和移位运算符。 与 C 语言中的算术运算符不同,Swift 中的算术运算符默认是不会溢出的。所有溢出行为都会被捕获并报告为错误。如果想让系统允许溢出行为,可以选择使用 Swift 中另一套默认支持溢出的运算符,比如溢出加法运算符(`&+`)。所有的这些溢出运算符都是以 `&` 开头的。 -自定义结构体、类和枚举时,如果也为它们提供标准 Swift 运算符的实现,将会非常有用。在 Swift 中自定义运算符非常简单,运算符也会针对不同类型使用对应实现。 +自定义结构体、类和枚举时,如果也为它们提供标准 Swift 运算符的实现,将会非常有用。在 Swift 中为这些运算符提供自定义的实现非常简单,运算符也会针对不同类型使用对应实现。 -我们不用被预定义的运算符所限制。在 Swift 中可以自由地定义中缀、前缀、后缀和赋值运算符,以及相应的优先级与结合性。这些运算符在代码中可以像预定义的运算符一样使用,我们甚至可以扩展已有的类型以支持自定义的运算符。 +我们不用被预定义的运算符所限制。在 Swift 中可以自由地定义中缀、前缀、后缀和赋值运算符,它们具有自定义的优先级与关联值。这些运算符在代码中可以像预定义的运算符一样使用,你甚至可以扩展已有的类型以支持自定义运算符。 ## 位运算符 @@ -16,31 +16,31 @@ Swift 支持 C 语言中的全部位运算符,接下来会一一介绍。 -### 按位取反运算符 +### Bitwise NOT Operator(按位取反运算符) -*按位取反运算符(`~`)*可以对一个数值的全部比特位进行取反: +*按位取反运算符(`~`)*对一个数值的全部比特位进行取反: -![Art/bitwiseNOT_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseNOT_2x.png) +![Art/bitwiseNOT_2x.png](https://docs.swift.org/swift-book/_images/bitwiseNOT_2x.png) -按位取反运算符是一个前缀运算符,需要直接放在运算的数之前,并且它们之间不能添加任何空格: +按位取反运算符是一个前缀运算符,直接放在运算数之前,并且它们之间不能添加任何空格: ```swift let initialBits: UInt8 = 0b00001111 let invertedBits = ~initialBits // 等于 0b11110000 ``` -`UInt8` 类型的整数有 8 个比特位,可以存储 `0 ~ 255` 之间的任意整数。这个例子初始化了一个 `UInt8` 类型的整数,并赋值为二进制的 `00001111`,它的前 4 位都为 `0`,后 4 位都为 `1`。这个值等价于十进制的 `15`。 +`UInt8` 类型的整数有 8 个比特位,可以存储 `0 ~ 255` 之间的任意整数。这个例子初始化了一个 `UInt8` 类型的整数,并赋值为二进制的 `00001111`,它的前 4 位为 `0`,后 4 位为 `1`。这个值等价于十进制的 `15`。 接着使用按位取反运算符创建了一个名为 `invertedBits` 的常量,这个常量的值与全部位取反后的 `initialBits` 相等。即所有的 `0` 都变成了 `1`,同时所有的 `1` 都变成 `0`。`invertedBits` 的二进制值为 `11110000`,等价于无符号十进制数的 `240`。 -### 按位与运算符 +### Bitwise AND Operator(按位与运算符) -*按位与运算符(`&`)*可以对两个数的比特位进行合并。它返回一个新的数,只有当两个数的对应位都为 `1` 的时候,新数的对应位才为 `1`: +*按位与运算符(`&`)*对两个数的比特位进行合并。它返回一个新的数,只有当两个数的对应位*都*为 `1` 的时候,新数的对应位才为 `1`: -![Art/bitwiseAND_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseAND_2x.png) +![Art/bitwiseAND_2x.png](https://docs.swift.org/swift-book/_images/bitwiseAND_2x.png) -在下面的示例当中,`firstSixBits` 和 `lastSixBits` 中间 4 个位的值都为 `1`。按位与运算符对它们进行了运算,得到二进制数值 `00111100`,等价于无符号十进制数的 `60`: +在下面的示例当中,`firstSixBits` 和 `lastSixBits` 中间 4 个位的值都为 `1`。使用按位与运算符之后,得到二进制数值 `00111100`,等价于无符号十进制数的 `60`: ```swift let firstSixBits: UInt8 = 0b11111100 @@ -49,13 +49,13 @@ let middleFourBits = firstSixBits & lastSixBits // 等于 00111100 ``` -### 按位或运算符 +### Bitwise OR Operator(按位或运算符) -*按位或运算符(`|`)*可以对两个数的比特位进行比较。它返回一个新的数,只要两个数的对应位中有任意一个为 `1` 时,新数的对应位就为 `1`: +*按位或运算符(`|`)*可以对两个数的比特位进行比较。它返回一个新的数,只要两个数的对应位中有*任意一个*为 `1` 时,新数的对应位就为 `1`: -![Art/bitwiseOR_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseOR_2x.png "Art/bitwiseOR_2x.png") +![Art/bitwiseOR_2x.png](https://docs.swift.org/swift-book/_images/bitwiseOR_2x.png) -在下面的示例中,`someBits` 和 `moreBits` 不同的位会被设置为 `1`。接位或运算符对它们进行了运算,得到二进制数值 `11111110`,等价于无符号十进制数的 `254`: +在下面的示例中,`someBits` 和 `moreBits` 存在不同的位被设置为 `1`。使用按位或运算符之后,得到二进制数值 `11111110`,等价于无符号十进制数的 `254`: ```swift let someBits: UInt8 = 0b10110010 @@ -64,13 +64,13 @@ let combinedbits = someBits | moreBits // 等于 11111110 ``` -### 按位异或运算符 +### Bitwise XOR Operator(按位异或运算符) -*按位异或运算符(`^`)*可以对两个数的比特位进行比较。它返回一个新的数,当两个数的对应位不相同时,新数的对应位就为 `1`: +*按位异或运算符*,或称“排外的或运算符”(`^`),可以对两个数的比特位进行比较。它返回一个新的数,当两个数的对应位不相同时,新数的对应位就为 `1`,并且对应位相同时则为 `0`: -![Art/bitwiseXOR_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseXOR_2x.png "Art/bitwiseXOR_2x.png") +![Art/bitwiseXOR_2x.png](https://docs.swift.org/swift-book/_images/bitwiseXOR_2x.png) -在下面的示例当中,`firstBits` 和 `otherBits` 都有一个自己的位为 `1` 而对方的对应位为 `0` 的位。 按位异或运算符将新数的这两个位都设置为 `1`,同时将其它位都设置为 `0`: +在下面的示例当中,`firstBits` 和 `otherBits` 都有一个自己为 `1`,而对方为 `0` 的位。按位异或运算符将新数的这两个位都设置为 `1`。在其余的位上 `firstBits` 和 `otherBits` 是相同的,所以设置为 `0`: ```swift let firstBits: UInt8 = 0b00010100 @@ -79,7 +79,7 @@ let outputBits = firstBits ^ otherBits // 等于 00010001 ``` -### 按位左移、右移运算符 +### Bitwise Left and Right Shift Operators(按位左移、右移运算符) *按位左移运算符(`<<`)*和*按位右移运算符(`>>`)*可以对一个数的所有位进行指定位数的左移和右移,但是需要遵守下面定义的规则。 @@ -90,15 +90,15 @@ let outputBits = firstBits ^ otherBits // 等于 00010001 对无符号整数进行移位的规则如下: -1. 已经存在的位按指定的位数进行左移和右移。 +1. 已存在的位按指定的位数进行左移和右移。 2. 任何因移动而超出整型存储范围的位都会被丢弃。 3. 用 `0` 来填充移位后产生的空白位。 这种方法称为*逻辑移位*。 -以下这张图展示了 `11111111 << 1`(即把 `11111111` 向左移动 `1` 位),和 `11111111 >> 1`(即把 `11111111` 向右移动 `1` 位)的结果。蓝色的部分是被移位的,灰色的部分是被抛弃的,橙色的部分则是被填充进来的: +以下这张图展示了 `11111111 << 1`(即把 `11111111` 向左移动 `1` 位),和 `11111111 >> 1`(即把 `11111111` 向右移动 `1` 位)的结果。蓝色的数字是被移位的,灰色的数字是被抛弃的,橙色的 `0` 则是被填充进来的: -![Art/bitshiftUnsigned_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftUnsigned_2x.png "Art/bitshiftUnsigned_2x.png") +![Art/bitshiftUnsigned_2x.png](https://docs.swift.org/swift-book/_images/bitshiftUnsigned_2x.png) 下面的代码演示了 Swift 中的移位运算: @@ -120,50 +120,48 @@ let greenComponent = (pink & 0x00FF00) >> 8 // greenComponent 是 0x66, 即 10 let blueComponent = pink & 0x0000FF // blueComponent 是 0x99,即 153 ``` -这个示例使用了一个命名为 `pink` 的 `UInt32` 型常量来存储 CSS 中粉色的颜色值。该 CSS 的十六进制颜色值 `#CC6699`,在 Swift 中表示为 `0xCC6699`。然后利用按位与运算符(`&`)和按位右移运算符(`>>`)从这个颜色值中分解出红(`CC`)、绿(`66`)以及蓝(`99`)三个部分。 +这个示例使用了一个命名为 `pink` 的 `UInt32` 型常量来存储 Cascading Style Sheets(CSS)中粉色的颜色值。该 CSS 的颜色值 `#CC6699`,在 Swift 中表示为十六进制的 `0xCC6699`。然后利用按位与运算符(`&`)和按位右移运算符(`>>`)从这个颜色值中分解出红(`CC`)、绿(`66`)以及蓝(`99`)三个部分。 红色部分是通过对 `0xCC6699` 和 `0xFF0000` 进行按位与运算后得到的。`0xFF0000` 中的 `0` 部分“掩盖”了 `OxCC6699` 中的第二、第三个字节,使得数值中的 `6699` 被忽略,只留下 `0xCC0000`。 -然后,再将这个数按向右移动 16 位(`>> 16`)。十六进制中每两个字符表示 8 个比特位,所以移动 16 位后 `0xCC0000` 就变为 `0x0000CC`。这个数和 `0xCC` 是等同的,也就是十进制数值的 `204`。 +然后,将这个数向右移动 16 位(`>> 16`)。十六进制中每两个字符占用 8 个比特位,所以移动 16 位后 `0xCC0000` 就变为 `0x0000CC`。这个数和 `0xCC` 是等同的,也就是十进制数值的 `204`。 同样的,绿色部分通过对 `0xCC6699` 和 `0x00FF00` 进行按位与运算得到 `0x006600`。然后将这个数向右移动 8 位,得到 `0x66`,也就是十进制数值的 `102`。 -最后,蓝色部分通过对 `0xCC6699` 和 `0x0000FF` 进行按位与运算得到 `0x000099`。这里不需要再向右移位,所以结果为 `0x99` ,也就是十进制数值的 `153`。 +最后,蓝色部分通过对 `0xCC6699` 和 `0x0000FF` 进行按位与运算得到 `0x000099`。这里不需要再向右移位,而 `0x000099` 也就是 `0x99` ,也就是十进制数值的 `153`。 #### 有符号整数的移位运算 -对比无符号整数,有符号整数的移位运算相对复杂得多,这种复杂性源于有符号整数的二进制表现形式。(为了简单起见,以下的示例都是基于 8 比特位的有符号整数的,但是其中的原理对任何位数的有符号整数都是通用的。) +对比无符号整数,有符号整数的移位运算相对复杂得多,这种复杂性源于有符号整数的二进制表现形式。(为了简单起见,以下的示例都是基于 8 比特的有符号整数,但是其中的原理对任何位数的有符号整数都是通用的。) -有符号整数使用第 1 个比特位(通常被称为符号位)来表示这个数的正负。符号位为 `0` 代表正数,为 `1` 代表负数。 +有符号整数使用第 1 个比特位(通常被称为*符号位*)来表示这个数的正负。符号位为 `0` 代表正数,为 `1` 代表负数。 -其余的比特位(通常被称为数值位)存储了实际的值。有符号正整数和无符号数的存储方式是一样的,都是从 `0` 开始算起。这是值为 `4` 的 `Int8` 型整数的二进制位表现形式: +其余的比特位(通常被称为*数值位*)存储了实际的值。有符号正整数和无符号数的存储方式是一样的,都是从 `0` 开始算起。这是值为 `4` 的 `Int8` 型整数的二进制位表现形式: -![Art/bitshiftSignedFour_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSignedFour_2x.png "Art/bitshiftSignedFour_2x.png") +![Art/bitshiftSignedFour_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSignedFour_2x.png) -符号位为 `0`,说明这是一个正数,另外 7 位则代表了十进制数值 `4` 的二进制表示。 +符号位为 `0`(代表这是一个“正数”),另外 7 位则代表了十进制数值 `4` 的二进制表示。 -负数的存储方式略有不同。它存储的值的绝对值等于 `2` 的 `n` 次方减去它的实际值(也就是数值位表示的值),这里的 `n` 为数值位的比特位数。一个 8 比特位的数有 7 个比特位是数值位,所以是 `2` 的 `7` 次方,即 `128`。 +负数的存储方式略有不同。它存储 `2` 的 `n` 次方减去其实际值的绝对值,这里的 `n` 是数值位的位数。一个 8 比特位的数有 7 个比特位是数值位,所以是 `2` 的 `7` 次方,即 `128`。 -这是值为 `-4` 的 `Int8` 型整数的二进制位表现形式: +这是值为 `-4` 的 `Int8` 型整数的二进制表现形式: -![Art/bitshiftSignedMinusFour_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSignedMinusFour_2x.png "Art/bitshiftSignedMinusFour_2x.png") +![Art/bitshiftSignedMinusFour_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSignedMinusFour_2x.png) 这次的符号位为 `1`,说明这是一个负数,另外 7 个位则代表了数值 `124`(即 `128 - 4`)的二进制表示: -![Art/bitshiftSignedMinusFourValue_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSignedMinusFourValue_2x.png "Art/bitshiftSignedMinusFourValue_2x.png") +![Art/bitshiftSignedMinusFourValue_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSignedMinusFourValue_2x.png) -负数的表示通常被称为*二进制补码*表示。用这种方法来表示负数乍看起来有点奇怪,但它有几个优点。 +负数的表示通常被称为*二进制补码*。用这种方法来表示负数乍看起来有点奇怪,但它有几个优点。 -首先,如果想对 `-1` 和 `-4` 进行加法运算,我们只需要将这两个数的全部 8 个比特位进行相加,并且将计算结果中超出 8 位的数值丢弃: +首先,如果想对 `-1` 和 `-4` 进行加法运算,我们只需要对这两个数的全部 8 个比特位执行标准的二进制相加(包括符号位),并且将计算结果中超出 8 位的数值丢弃: -![Art/bitshiftSignedAddition_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSignedAddition_2x.png "Art/bitshiftSignedAddition_2x.png") +![Art/bitshiftSignedAddition_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSignedAddition_2x.png) -其次,使用二进制补码可以使负数的按位左移和右移运算得到跟正数同样的效果,即每向左移一位就将自身的数值乘以 2,每向右一位就将自身的数值除以 2。要达到此目的,对有符号整数的右移有一个额外的规则: +其次,使用二进制补码可以使负数的按位左移和右移运算得到跟正数同样的效果,即每向左移一位就将自身的数值乘以 2,每向右一位就将自身的数值除以 2。要达到此目的,对有符号整数的右移有一个额外的规则:当对有符号整数进行按位右移运算时,遵循与无符号整数相同的规则,但是对于移位产生的空白位使用*符号位*进行填充,而不是用 `0`。 -* 当对整数进行按位右移运算时,遵循与无符号整数相同的规则,但是对于移位产生的空白位使用*符号位*进行填充,而不是用 `0`。 - -![Art/bitshiftSigned_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSigned_2x.png "Art/bitshiftSigned_2x.png") +![Art/bitshiftSigned_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSigned_2x.png) 这个行为可以确保有符号整数的符号位不会因为右移运算而改变,这通常被称为*算术移位*。 @@ -172,9 +170,9 @@ let blueComponent = pink & 0x0000FF // blueComponent 是 0x99,即 153 ## 溢出运算符 -在默认情况下,当向一个整数赋予超过它容量的值时,Swift 默认会报错,而不是生成一个无效的数。这个行为为我们在运算过大或着过小的数的时候提供了额外的安全性。 +当向一个整数类型的常量或者变量赋予超过它容量的值时,Swift 默认会报错,而不是允许生成一个无效的数。这个行为为我们在运算过大或者过小的数时提供了额外的安全性。 -例如,`Int16` 型整数能容纳的有符号整数范围是 `-32768` 到 `32767`,当为一个 `Int16` 型变量赋的值超过这个范围时,系统就会报错: +例如,`Int16` 型整数能容纳的有符号整数范围是 `-32768` 到 `32767`。当为一个 `Int16` 类型的变量或常量赋予的值超过这个范围时,系统就会报错: ```swift var potentialOverflow = Int16.max @@ -183,9 +181,9 @@ potentialOverflow += 1 // 这里会报错 ``` -为过大或者过小的数值提供错误处理,能让我们在处理边界值时更加灵活。 +在赋值时为过大或者过小的情况提供错误处理,能让我们在处理边界值时更加灵活。 -然而,也可以选择让系统在数值溢出的时候采取截断处理,而非报错。可以使用 Swift 提供的三个溢出运算符来让系统支持整数溢出运算。这些运算符都是以 `&` 开头的: +然而,当你希望的时候也可以选择让系统在数值溢出的时候采取截断处理,而非报错。Swift 提供的三个*溢出运算符*来让系统支持整数溢出运算。这些运算符都是以 `&` 开头的: * 溢出加法 `&+` * 溢出减法 `&-` @@ -205,11 +203,11 @@ unsignedOverflow = unsignedOverflow &+ 1 // 此时 unsignedOverflow 等于 0 ``` -`unsignedOverflow` 被初始化为 `UInt8` 所能容纳的最大整数(`255`,以二进制表示即 `11111111`)。然后使用了溢出加法运算符(`&+`)对其进行加 `1` 运算。这使得它的二进制表示正好超出 `UInt8` 所能容纳的位数,也就导致了数值的溢出,如下图所示。数值溢出后,留在 `UInt8` 边界内的值是 `00000000`,也就是十进制数值的 `0`。 +`unsignedOverflow` 被初始化为 `UInt8` 所能容纳的最大整数(`255`,以二进制表示即 `11111111`)。然后使用溢出加法运算符(`&+`)对其进行加 `1` 运算。这使得它的二进制表示正好超出 `UInt8` 所能容纳的位数,也就导致了数值的溢出,如下图所示。数值溢出后,仍然留在 `UInt8` 边界内的值是 `00000000`,也就是十进制数值的 `0`。 -![Art/overflowAddition_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/overflowAddition_2x.png "Art/overflowAddition_2x.png") +![Art/overflowAddition_2x.png](https://docs.swift.org/swift-book/_images/overflowAddition_2x.png) -同样地,当我们对一个无符号整数使用溢出减法(`&-`)进行下溢运算时也会产生类似的现象: +当允许对一个无符号整数进行下溢运算时也会产生类似的情况。这里有一个使用溢出减法运算符(`&-`)的例子: ```swift var unsignedOverflow = UInt8.min @@ -220,9 +218,9 @@ unsignedOverflow = unsignedOverflow &- 1 `UInt8` 型整数能容纳的最小值是 `0`,以二进制表示即 `00000000`。当使用溢出减法运算符对其进行减 `1` 运算时,数值会产生下溢并被截断为 `11111111`, 也就是十进制数值的 `255`。 -![Art/overflowUnsignedSubtraction_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/overflowUnsignedSubtraction_2x.png "Art/overflowAddition_2x.png") +![Art/overflowUnsignedSubtraction_2x.png](https://docs.swift.org/swift-book/_images/overflowUnsignedSubtraction_2x.png) -溢出也会发生在有符号整型数值上。在对有符号整型数值进行溢出加法或溢出减法运算时,符号位也需要参与计算,正如[按位左移、右移运算符](#bitwise_left_and_right_shift_operators)所描述的。 +溢出也会发生在有符号整型上。针对有符号整型的所有溢出加法或者减法运算都是按位运算的方式执行的,符号位也需要参与计算,正如[按位左移、右移运算符](#bitwise_left_and_right_shift_operators)所描述的。 ```swift var signedOverflow = Int8.min @@ -231,27 +229,27 @@ signedOverflow = signedOverflow &- 1 // 此时 signedOverflow 等于 127 ``` -`Int8` 型整数能容纳的最小值是 `-128`,以二进制表示即 `10000000`。当使用溢出减法运算符对其进行减 `1` 运算时,符号位被翻转,得到二进制数值 `01111111`,也就是十进制数值的 `127`,这个值也是 `Int8` 型整数所能容纳的最大值。 +`Int8` 型整数能容纳的最小值是 `-128`,以二进制表示即 `10000000`。当使用溢出减法运算符对其进行减 `1` 运算时,符号位被翻转,得到二进制数值 `01111111`,也就是十进制数值的 `127`,这个值也是 `Int8` 型整所能容纳的最大值。 -![Art/overflowSignedSubtraction_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/overflowSignedSubtraction_2x.png "Art/overflowSignedSubtraction_2x.png") +![Art/overflowSignedSubtraction_2x.png](https://docs.swift.org/swift-book/_images/overflowSignedSubtraction_2x.png) -对于无符号与有符号整型数值来说,当出现上溢时,它们会从数值所能容纳的最大数变成最小的数。同样地,当发生下溢时,它们会从所能容纳的最小数变成最大的数。 +对于无符号与有符号整型数值来说,当出现上溢时,它们会从数值所能容纳的最大数变成最小数。同样地,当发生下溢时,它们会从所能容纳的最小数变成最大数。 ## 优先级和结合性 -运算符的*优先级*使得一些运算符优先于其他运算符,高优先级的运算符会先被计算。 +运算符的*优先级*使得一些运算符优先于其他运算符;它们会先被执行。 -*结合性*定义了相同优先级的运算符是如何结合的,也就是说,是与左边结合为一组,还是与右边结合为一组。可以将这意思理解为“它们是与左边的表达式结合的”或者“它们是与右边的表达式结合的”。 +*结合性*定义了相同优先级的运算符是如何结合的,也就是说,是与左边结合为一组,还是与右边结合为一组。可以将其理解为“它们是与左边的表达式结合的,”或者“它们是与右边的表达式结合的。” -在复合表达式的运算顺序中,运算符的优先级和结合性是非常重要的。举例来说,运算符优先级解释了为什么下面这个表达式的运算结果会是 `17`。 +当考虑一个复合表达式的计算顺序时,运算符的优先级和结合性是非常重要的。举例来说,运算符优先级解释了为什么下面这个表达式的运算结果会是 `17`。 ```swift 2 + 3 % 4 * 5 // 结果是 17 ``` -如果完全从左到右进行运算,则运算的过程是这样的: +如果你直接从左到右进行运算,你可能认为运算的过程是这样的: - 2 + 3 = 5 - 5 % 4 = 1 @@ -259,7 +257,7 @@ signedOverflow = signedOverflow &- 1 但是正确答案是 `17` 而不是 `5`。优先级高的运算符要先于优先级低的运算符进行计算。与 C 语言类似,在 Swift 中,乘法运算符(`*`)与取余运算符(`%`)的优先级高于加法运算符(`+`)。因此,它们的计算顺序要先于加法运算。 -而乘法与取余的优先级相同。这时为了得到正确的运算顺序,还需要考虑结合性。乘法与取余运算都是左结合的。可以将这考虑成为这两部分表达式都隐式地加上了括号: +而乘法运算与取余运算的优先级*相同*。这时为了得到正确的运算顺序,还需要考虑结合性。乘法运算与取余运算都是左结合的。可以将这考虑成,从它们的左边开始为这两部分表达式都隐式地加上括号: ```swift 2 + ((3 % 4) * 5) @@ -277,22 +275,22 @@ signedOverflow = signedOverflow &- 1 2 + 15 ``` -因此计算结果为 `17`。 +因此计算结果为 `17`。 -如果想查看完整的 Swift 运算符优先级和结合性规则,请参考[表达式](../chapter3/04_Expressions.html)。如果想查看 Swift 标准库提供所有的运算符,请查看 [Swift Standard Library Operators Reference](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。 +有关 Swift 标准库提供的操作符信息,包括操作符优先级组和结核性设置的完整列表,请参见[操作符声明](https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations)。 > 注意 > -> 相对 C 语言和 Objective-C 来说,Swift 的运算符优先级和结合性规则更加简洁和可预测。但是,这也意味着它们相较于 C 语言及其衍生语言并不是完全一致的。在对现有的代码进行移植的时候,要注意确保运算符的行为仍然符合你的预期。 +> 相对 C 语言和 Objective-C 来说,Swift 的运算符优先级和结合性规则更加简洁和可预测。但是,这也意味着它们相较于 C 语言及其衍生语言并不是完全一致。在对现有的代码进行移植的时候,要注意确保运算符的行为仍然符合你的预期。 ## 运算符函数 -类和结构体可以为现有的运算符提供自定义的实现,这通常被称为*运算符重载*。 +类和结构体可以为现有的运算符提供自定义的实现。这通常被称为运算符*重载*。 -下面的例子展示了如何为自定义的结构体实现加法运算符(`+`)。算术加法运算符是一个双目运算符,因为它可以对两个值进行运算,同时它还是中缀运算符,因为它出现在两个值中间。 +下面的例子展示了如何让自定义的结构体支持加法运算符(`+`)。算术加法运算符是一个*二元运算符*,因为它是对两个值进行运算,同时它还可以称为*中缀*运算符,因为它出现在两个值中间。 -例子中定义了一个名为 `Vector2D` 的结构体用来表示二维坐标向量 `(x, y)`,紧接着定义了一个可以对两个 `Vector2D` 结构体进行相加的运算符函数: +例子中定义了一个名为 `Vector2D` 的结构体用来表示二维坐标向量 `(x, y)`,紧接着定义了一个可以将两个 `Vector2D` 结构体实例进行相加的*运算符函数*: ```swift struct Vector2D { @@ -306,7 +304,7 @@ extension Vector2D { } ``` -该运算符函数被定义为 `Vector2D` 上的一个类方法,并且函数的名字与它要进行重载的 `+` 名字一致。因为加法运算并不是一个向量必需的功能,所以这个类方法被定义在 `Vector2D` 的一个扩展中,而不是 `Vector2D` 结构体声明内。而算术加法运算符是双目运算符,所以这个运算符函数接收两个类型为 `Vector2D` 的参数,同时有一个 `Vector2D` 类型的返回值。 +该运算符函数被定义为 `Vector2D` 上的一个类方法,并且函数的名字与它要进行重载的 `+` 名字一致。因为加法运算并不是一个向量必需的功能,所以这个类方法被定义在 `Vector2D` 的一个扩展中,而不是 `Vector2D` 结构体声明内。而算术加法运算符是二元运算符,所以这个运算符函数接收两个类型为 `Vector2D` 的参数,同时有一个 `Vector2D` 类型的返回值。 在这个实现中,输入参数分别被命名为 `left` 和 `right`,代表在 `+` 运算符左边和右边的两个 `Vector2D` 实例。函数返回了一个新的 `Vector2D` 实例,这个实例的 `x` 和 `y` 分别等于作为参数的两个实例的 `x` 和 `y` 的值之和。 @@ -321,12 +319,12 @@ let combinedVector = vector + anotherVector 这个例子实现两个向量 `(3.0,1.0)` 和 `(2.0,4.0)` 的相加,并得到新的向量 `(5.0,5.0)`。这个过程如下图示: -![Art/vectorAddition_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/vectorAddition_2x.png "Art/vectorAddition_2x.png") +![Art/vectorAddition_2x.png](https://docs.swift.org/swift-book/_images/vectorAddition_2x.png) ### 前缀和后缀运算符 -上个例子演示了一个双目中缀运算符的自定义实现。类与结构体也能提供标准*单目运算符*的实现。单目运算符只运算一个值。当运算符出现在值之前时,它就是*前缀*的(例如 `-a`),而当它出现在值之后时,它就是*后缀*的(例如 `b!`)。 +上个例子演示了一个二元中缀运算符的自定义实现。类与结构体也能提供标准*一元运算符*的实现。一元运算符只运算一个值。当运算符出现在值之前时,它就是*前缀*的(例如 `-a`),而当它出现在值之后时,它就是*后缀*的(例如 `b!`)。 要实现前缀或者后缀运算符,需要在声明运算符函数的时候在 `func` 关键字之前指定 `prefix` 或者 `postfix` 修饰符: @@ -338,9 +336,9 @@ extension Vector2D { } ``` -这段代码为 `Vector2D` 类型实现了单目负号运算符。由于该运算符是前缀运算符,所以这个函数需要加上 `prefix` 修饰符。 +这段代码为 `Vector2D` 类型实现了一元运算符(`-a`)。由于该运算符是前缀运算符,所以这个函数需要加上 `prefix` 修饰符。 -对于简单数值,单目负号运算符可以对它们的正负性进行改变。对于 `Vector2D` 来说,该运算将其 `x` 和 `y` 属性的正负性都进行了改变: +对于简单数值,一元负号运算符可以对它们的正负性进行改变。对于 `Vector2D` 来说,该运算将其 `x` 和 `y` 属性的正负性都进行了改变: ```swift let positive = Vector2D(x: 3.0, y: 4.0) @@ -353,7 +351,9 @@ let alsoPositive = -negative ### 复合赋值运算符 -*复合赋值运算符*将赋值运算符(`=`)与其它运算符进行结合。例如,将加法与赋值结合成加法赋值运算符(`+=`)。在实现的时候,需要把运算符的左参数设置成 `inout` 类型,因为这个参数的值会在运算符函数内直接被修改。 +*复合赋值运算符*将赋值运算符(`=`)与其它运算符进行结合。例如,将加法与赋值结合成加法赋值运算符(`+=`)。在实现的时候,需要把运算符的左参数设置成 `inout` 类型,因为这个参数的值会在运算符函数内直接被修改。 + +在下面的例子中,对 `Vector2D` 实例实现了一个加法赋值运算符函数: ```swift extension Vector2D { @@ -374,14 +374,14 @@ original += vectorToAdd > 注意 > -> 不能对默认的赋值运算符(`=`)进行重载。只有组合赋值运算符可以被重载。同样地,也无法对三目条件运算符 (`a ? b : c`) 进行重载。 +> 不能对默认的赋值运算符(`=`)进行重载。只有复合赋值运算符可以被重载。同样地,也无法对三元条件运算符 (`a ? b : c`) 进行重载。 ### 等价运算符 -自定义的类和结构体没有对*等价运算符*进行默认实现,等价运算符通常被称为“相等”运算符(`==`)与“不等”运算符(`!=`)。对于自定义类型,Swift 无法判断其是否“相等”,因为“相等”的含义取决于这些自定义类型在你的代码中所扮演的角色。 +通常情况下,自定义的类和结构体没有对*等价运算符*进行默认实现,等价运算符通常被称为*相等*运算符(`==`)与*不等*运算符(`!=`)。 -为了使用等价运算符能对自定义的类型进行判等运算,需要为其提供自定义实现,实现的方法与其它中缀运算符一样, 并且增加对标准库 `Equatable` 协议的遵循: +为了使用等价运算符对自定义的类型进行判等运算,需要为“相等”运算符提供自定义实现,实现的方法与其它中缀运算符一样, 并且增加对标准库 `Equatable` 协议的遵循: ```swift extension Vector2D: Equatable { @@ -391,7 +391,7 @@ extension Vector2D: Equatable { } ``` -上述代码实现了“相等”运算符(`==`)来判断两个 `Vector2D` 实例是否相等。对于 `Vector2D` 类型来说,“相等”意味着“两个实例的 `x` 属性和 `y` 属性都相等”,这也是代码中用来进行判等的逻辑。示例里同时也实现了“不等”运算符(`!=`),它简单地将“相等”运算符的结果进行取反后返回。 +上述代码实现了“相等”运算符(`==`)来判断两个 `Vector2D` 实例是否相等。对于 `Vector2D` 来说,“相等”意味着“两个实例的 `x` 和 `y` 都相等”,这也是代码中用来进行判等的逻辑。如果你已经实现了“相等”运算符,通常情况下你并不需要自己再去实现“不等”运算符(`!=`)。标准库对于“不等”运算符提供了默认的实现,它简单地将“相等”运算符的结果进行取反后返回。 现在我们可以使用这两个运算符来判断两个 `Vector2D` 实例是否相等: @@ -404,15 +404,15 @@ if twoThree == anotherTwoThree { // 打印 “These two vectors are equivalent.” ``` -Swift 为以下自定义类型提等价运算符供合成实现: +Swift 为以下数种自定义类型提供等价运算符的默认实现: -- 只拥有遵循 `Equatable` 协议存储属性的结构体; -- 只拥有遵循 `Equatable` 协议关联类型的枚举; -- 没有关联类型的枚举。 +- 只拥有存储属性,并且它们全都遵循 `Equatable` 协议的结构体 +- 只拥有关联类型,并且它们全都遵循 `Equatable` 协议的枚举 +- 没有关联类型的枚举 -在类型原本的声明中声明遵循 `Equatable` 来接收这些默认实现。 +在类型原始的声明中声明遵循 `Equatable` 来接收这些默认实现。 -下面为三维位置向量 `(x, y, z)` 定义的 `Vector3D` 结构体,与 `Vector2D` 类似,由于 `x`,`y` 和 `z` 属性都是 `Equatable` 类型,`Vector3D` 就收到默认的等价运算符实现了。 +下面为三维位置向量 `(x, y, z)` 定义的 `Vector3D` 结构体,与 `Vector2D` 类似。由于 `x`,`y` 和 `z` 属性都是 `Equatable` 类型,`Vector3D` 获得了默认的等价运算符实现。 ```swift struct Vector3D: Equatable { @@ -438,7 +438,7 @@ if twoThreeFour == anotherTwoThreeFour { prefix operator +++ ``` -上面的代码定义了一个新的名为 `+++` 的前缀运算符。对于这个运算符,在 Swift 中并没有意义,因此我们针对 `Vector2D` 的实例来定义它的意义。对这个示例来讲,`+++` 被实现为“前缀双自增”运算符。它使用了前面定义的复合加法运算符来让矩阵对自身进行相加,从而让 `Vector2D` 实例的 `x` 属性和 `y` 属性的值翻倍。实现 `+++` 运算符的方式如下: +上面的代码定义了一个新的名为 `+++` 的前缀运算符。对于这个运算符,在 Swift 中并没有已知的意义,因此在针对 `Vector2D` 实例的特定上下文中,给予了它自定义的意义。对这个示例来讲,`+++` 被实现为“前缀双自增”运算符。它使用了前面定义的复合加法运算符来让矩阵与自身进行相加,从而让 `Vector2D` 实例的 `x` 属性和 `y` 属性值翻倍。你可以像下面这样通过对 `Vector2D` 添加一个 `+++` 类方法,来实现 `+++` 运算符: ```swift extension Vector2D { @@ -457,9 +457,9 @@ let afterDoubling = +++toBeDoubled ### 自定义中缀运算符的优先级 -每个自定义中缀运算符都属于某个优先级组。这个优先级组指定了这个运算符和其他中缀运算符的优先级和结合性。[优先级和结合性](#precedence_and_associativity)中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。 +每个自定义中缀运算符都属于某个优先级组。优先级组指定了这个运算符相对于其他中缀运算符的优先级和结合性。[优先级和结合性](#precedence_and_associativity)中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。 -而没有明确放入优先级组的自定义中缀运算符会放到一个默认的优先级组内,其优先级高于三元运算符。 +而没有明确放入某个优先级组的自定义中缀运算符将会被放到一个默认的优先级组内,其优先级高于三元运算符。 以下例子定义了一个新的自定义中缀运算符 `+-`,此运算符属于 `AdditionPrecedence` 优先组: @@ -476,8 +476,26 @@ let plusMinusVector = firstVector +- secondVector // plusMinusVector 是一个 Vector2D 实例,并且它的值为 (4.0, -2.0) ``` -这个运算符把两个向量的 `x` 值相加,同时用第一个向量的 `y` 值减去第二个向量的 `y` 值。因为它本质上是属于“相加型”运算符,所以将它放置 `+` 和 `-` 等默认的中缀“相加型”运算符相同的优先级组中。关于 Swift 标准库提供的运算符,以及完整的运算符优先级组和结合性设置,请参考 [Swift Standard Library Operators Reference](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。而更多关于优先级组以及自定义操作符和优先级组的语法,请参考[运算符声明](#operator_declaration) +这个运算符把两个向量的 `x` 值相加,同时从第一个向量的 `y` 中减去第二个向量的 `y` 。因为它本质上是属于“相加型”运算符,所以将它放置在 `+` 和 `-` 等默认中缀“相加型”运算符相同的优先级组中。关于 Swift 标准库提供的运算符,以及完整的运算符优先级组和结合性设置,请参考 [Operator Declarations](https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations)。而更多关于优先级组以及自定义操作符和优先级组的语法,请参考[Operator Declaration](https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID380) > 注意 > > 当定义前缀与后缀运算符的时候,我们并没有指定优先级。然而,如果对同一个值同时使用前缀与后缀运算符,则后缀运算符会先参与运算。 + + + + + + + + + + + + + + + + + + From ddda95cc6b4a1f7751aeb0808b38e5ea3fbd91d5 Mon Sep 17 00:00:00 2001 From: dzy_PC Date: Mon, 31 Dec 2018 14:13:35 +0800 Subject: [PATCH 4/8] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E7=A9=BA=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/26_Advanced_Operators.md | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/source/chapter2/26_Advanced_Operators.md b/source/chapter2/26_Advanced_Operators.md index e5aedd73..9639a98d 100644 --- a/source/chapter2/26_Advanced_Operators.md +++ b/source/chapter2/26_Advanced_Operators.md @@ -480,22 +480,4 @@ let plusMinusVector = firstVector +- secondVector > 注意 > -> 当定义前缀与后缀运算符的时候,我们并没有指定优先级。然而,如果对同一个值同时使用前缀与后缀运算符,则后缀运算符会先参与运算。 - - - - - - - - - - - - - - - - - - +> 当定义前缀与后缀运算符的时候,我们并没有指定优先级。然而,如果对同一个值同时使用前缀与后缀运算符,则后缀运算符会先参与运算。 \ No newline at end of file From d29d719469f502c06ecd0c784d1e16737ae1c60b Mon Sep 17 00:00:00 2001 From: edz Date: Wed, 27 Mar 2019 14:19:47 +0800 Subject: [PATCH 5/8] =?UTF-8?q?Swift5=20=E6=96=B0=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E7=BB=B4=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter3/03_Types.md | 50 +++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/source/chapter3/03_Types.md b/source/chapter3/03_Types.md index 8245cf89..016816d5 100644 --- a/source/chapter3/03_Types.md +++ b/source/chapter3/03_Types.md @@ -15,11 +15,11 @@ - [类型继承子句](#type_inheritance_clause) - [类型推断](#type_inference) -Swift 语言存在两种类型:命名型类型和复合型类型。命名型类型是指定义时可以给定名字的类型。命名型类型包括类、结构体、枚举和协议。比如,一个用户定义的类 `MyClass` 的实例拥有类型 `MyClass`。除了用户定义的命名型类型,Swift 标准库也定义了很多常用的命名型类型,包括那些表示数组、字典和可选值的类型。 +Swift 语言存在两种类型:命名型类型和复合型类型。*命名型类型*是指定义时可以给定名字的类型。命名型类型包括类、结构体、枚举和协议。比如,一个用户定义类 `MyClass` 的实例拥有类型 `MyClass`。除了用户定义的命名型类型,Swift 标准库也定义了很多常用的命名型类型,包括那些表示数组、字典和可选值的类型。 -那些通常被其它语言认为是基本或原始的数据型类型,比如表示数字、字符和字符串的类型,实际上就是命名型类型,这些类型在 Swift 标准库中是使用结构体来定义和实现的。因为它们是命名型类型,因此你可以按照 [扩展](../chapter2/21_Extensions.html) 和 [扩展声明](05_Declarations.html#extension_declaration) 中讨论的那样,声明一个扩展来增加它们的行为以满足你程序的需求。 +那些通常被其它语言认为是基本或原始的数据型类型,比如表示数字、字符和字符串的类型,实际上就是命名型类型,这些类型在 Swift 标准库中是使用结构体来定义和实现的。因为它们是命名型类型,因此你可以按照 [扩展](../chapter2/20_Extensions.html) 和 [扩展声明](06_Declarations.html#extension_declaration) 中讨论的那样,声明一个扩展来增加它们的行为以满足你程序的需求。 -复合型类型是没有名字的类型,它由 Swift 本身定义。Swift 存在两种复合型类型:函数类型和元组类型。一个复合型类型可以包含命名型类型和其它复合型类型。例如,元组类型 `(Int, (Int, Int))` 包含两个元素:第一个是命名型类型 `Int`,第二个是另一个复合型类型 `(Int, Int)`。 +*复合型类型*是没有名字的类型,它由 Swift 本身定义。Swift 存在两种复合型类型:函数类型和元组类型。一个复合型类型可以包含命名型类型和其它复合型类型。例如,元组类型 `(Int, (Int, Int))` 包含两个元素:第一个是命名型类型 `Int`,第二个是另一个复合型类型 `(Int, Int)`。 你可以在命名型类型和复合型类型使用小括号。但是在类型旁加小括号没有任何作用。举个例子,`(Int)` 等同于 `Int`。 @@ -44,7 +44,7 @@ Swift 语言存在两种类型:命名型类型和复合型类型。命名型 ## 类型注解 -类型注解显式地指定一个变量或表达式的值。类型注解始于冒号 `:` 终于类型,比如下面两个例子: +*类型注解*显式地指定一个变量或表达式的类型。类型注解始于冒号 `:` 终于类型,比如下面两个例子: ```swift let someTuple: (Double, Double) = (3.14159, 2.71828) @@ -58,7 +58,7 @@ func someFunction(a: Int) { /* ... */ } > 类型注解语法 > -> *类型注解* → **:** [*特性列表*](06_Attributes.html#attributes)可选 **输入输出参数**可选 [*类型*](#type) +> *类型注解* → **:** [*特性列表*](07_Attributes.html#attributes)可选 **输入输出参数**可选 [*类型*](#type) ## 类型标识符 @@ -83,7 +83,7 @@ var someValue: ExampleModule.MyType > 类型标识符语法 > -> *类型标识符* → [*类型名称*](#type-name) [*泛型参数子句*](08_Generic_Parameters_and_Arguments.html#generic_argument_clause)可选 | [*类型名称*](#type-name) [*泛型参数子句*](08_Generic_Parameters_and_Arguments.html#generic_argument_clause)可选 **.** [*类型标识符*](#type-identifier) +> *类型标识符* → [*类型名称*](#type-name) [*泛型参数子句*](09_Generic_Parameters_and_Arguments.html#generic_argument_clause)可选 | [*类型名称*](#type-name) [*泛型参数子句*](09_Generic_Parameters_and_Arguments.html#generic_argument_clause)可选 **.** [*类型标识符*](#type-identifier) > *类型名称* → [*标识符*](02_Lexical_Structure.html#identifier) @@ -108,7 +108,7 @@ someTuple = (left: 5, right: 5) // 错误:命名类型不匹配 > 元组类型语法 > -> *元组类型* → **(** [*元组类型元素列表*](#tuple-type-element-list) 可选 **)** +> *元组类型* → **(** **)** | **(** [*元组类型元素*](#tuple-type-element) **,** [*元组类型元素列表*](#tuple-type-element-list) **)** > *元组类型元素列表* → [*元组类型元素*](#tuple-type-element) | [*元组类型元素*](#tuple-type-element) **,** [*元组类型元素列表*](#tuple-type-element-list) @@ -121,17 +121,17 @@ someTuple = (left: 5, right: 5) // 错误:命名类型不匹配 函数类型表示一个函数、方法或闭包的类型,它由参数类型和返回值类型组成,中间用箭头(`->`)隔开: -> `参数类型` -> `返回值类型` +> (`参数类型`)->(`返回值类型`) -参数类型是由逗号间隔的类型列表。由于参数类型和返回值类型可以是元组类型,所以函数类型支持多参数与多返回值的函数与方法。 +*参数类型*是由逗号间隔的类型列表。由于*返回值类型*可以是元组类型,所以函数类型支持多返回值的函数与方法。 -你可以对函数参数 `() -> T`(其中 T 是任何类型)使用 `autoclosure` 特性。这会自动将参数表达式转化为闭包,表达式的结果即闭包返回值。这从语法结构上提供了一种便捷:延迟对表达式的求值,直到其值在函数体中被调用。以自动闭包做为参数的函数类型的例子详见 [自动闭包](../chapter2/07_Closures.html#autoclosures) 。 +你可以对参数类型为 `() -> T`(其中 T 是任何类型)的函数使用 `autoclosure` 特性。这会自动将参数表达式转化为闭包,表达式的结果即闭包返回值。这从语法结构上提供了一种便捷:延迟对表达式的求值,直到其值在函数体中被调用。以自动闭包做为参数的函数类型的例子详见 [自动闭包](../chapter2/07_Closures.html#autoclosures)。 -函数类型可以拥有一个可变长参数作为参数类型中的最后一个参数。从语法角度上讲,可变长参数由一个基础类型名字紧随三个点(`...`)组成,如 `Int...`。可变长参数被认为是一个包含了基础类型元素的数组。即 `Int...` 就是 `[Int]`。关于使用可变长参数的例子,请参阅 [可变参数](../chapter2/06_Functions.html#variadic_parameters)。 +函数类型可以拥有一个可变长参数作为*参数类型*中的最后一个参数。从语法角度上讲,可变长参数由一个基础类型名字紧随三个点(`...`)组成,如 `Int...`。可变长参数被认为是一个包含了基础类型元素的数组。即 `Int...` 就是 `[Int]`。关于使用可变长参数的例子,请参阅 [可变参数](../chapter2/06_Functions.html#variadic_parameters)。 为了指定一个 `in-out` 参数,可以在参数类型前加 `inout` 前缀。但是你不可以对可变长参数或返回值类型使用 `inout`。关于这种参数的详细讲解请参阅 [输入输出参数](../chapter2/06_Functions.html#in_out_parameters)。 -如果一个函数类型只有一个形式参数而且形式参数的类型是元组类型,那么元组类型在写函数类型的时候必须用圆括号括起来。比如说,`((Int, Int)) -> Void` 是接收一个元组 `(Int, Int)` 作为形式参数的函数的类型。与此相反,不加括号的 `(Int, Int) -> Void` 是一个接收两个 `Int` 形式参数并且不返回任何值的函数的类型。相似地,因为 `Void` 是空元组类型 `()` 的别名, 函数类型 `(Void)-> Void` 与一个空元组的变量的函数类型 `(()) -> ()` 是一样的。但这些类型和无变量的函数类型 `() -> ()` 是不一样的。 +如果一个函数类型只有一个形式参数而且形式参数的类型是元组类型,那么元组类型在写函数类型的时候必须用圆括号括起来。比如说,`((Int, Int)) -> Void` 是接收一个元组 `(Int, Int)` 作为形式参数并且不返回任何值的函数类型。与此相对,不加括号的 `(Int, Int) -> Void` 是一个接收两个 `Int` 作为形式参数并且不返回任何值的函数类型。相似地,因为 `Void` 是空元组类型 `()` 的别名,函数类型 `(Void)-> Void` 与 `(()) -> ()` 是一样的 - 一个将空元组作为唯一参数的函数。但这些类型和无变量的函数类型 `() -> ()` 是不一样的。 函数和方法中的变量名并不是函数类型的一部分。例如: @@ -159,13 +159,15 @@ var operation: (_ lhs: Int, _ rhs: Int) -> Int // 正确 var operation: (Int, Int) -> Int // 正确 ``` -如果一个函数类型包涵多个箭头(->),那么函数类型将从右向左进行组合。例如,函数类型 `Int -> Int -> Int` 可以理解为 `Int -> (Int -> Int)`,也就是说,该函数类型的参数为 `Int` 类型,其返回类型是一个参数类型为 `Int`,返回类型为 `Int` 的函数类型。 +如果一个函数类型包涵多个箭头(->),那么函数类型将从右向左进行组合。例如,函数类型 `(Int) -> (Int) -> Int` 可以理解为 `(Int) -> ((Int) -> Int)`,也就是说,该函数类型的参数为 `Int` 类型,其返回类型是一个参数类型为 `Int`,返回类型为 `Int` 的函数。 -函数类型若要抛出错误就必须使用 `throws` 关键字来标记,若要重抛错误则必须使用 `rethrows` 关键字来标记。`throws` 关键字是函数类型的一部分,非抛出函数是抛出函数函数的一个子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。抛出和重抛函数的相关描述见章节 [抛出函数与方法](05_Declarations.html#throwing_functions_and_methods) 和 [重抛函数与方法](05_Declarations.html#rethrowing_functions_and_methods)。 +函数类型若要抛出错误就必须使用 `throws` 关键字来标记,若要重抛错误则必须使用 `rethrows` 关键字来标记。`throws` 关键字是函数类型的一部分,非抛出函数是抛出函数函数的一个子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。抛出和重抛函数的相关描述见章节 [抛出函数与方法](06_Declarations.html#throwing_functions_and_methods) 和 [重抛函数与方法](06_Declarations.html#rethrowing_functions_and_methods)。 ### 对非逃逸闭包的限制 -非逃逸闭包函数不能作为参数传递到另一个非逃逸闭包函数的参数。这样的限制可以让 Swift 在编译时就完成更多的内存访问冲突检查, 而不是在运行时。举个例子: +当非逃逸闭包函数是参数时,不能存储在属性、变量或任何 `Any` 类型的常量中,因为这可能导致值的逃逸。 + +当非逃逸闭包函数是参数时,不能作为参数传递到另一个非逃逸闭包函数中。这样的限制可以让 Swift 在编译时就完成更多的内存访问冲突检查,而不是在运行时。举个例子: ```swift let external: (Any) -> Void = { _ in () } @@ -181,25 +183,25 @@ func takesTwoFunctions(first: (Any) -> Void, second: (Any) -> Void) { } ``` -在上面代码里,`takesTwoFunctions(first:second:)` 的两个参数都是函数。 它们都没有标记为 `@escaping`, 因此它们都是非逃逸的。 +在上面代码里,`takesTwoFunctions(first:second:)` 的两个参数都是函数。它们都没有标记为 `@escaping`, 因此它们都是非逃逸的。 -上述例子里的被标记为“错误”的四个函数调用会产生编译错误。因为第一个和第二个参数是非逃逸函数,它们不能够被当作变量被传递到另一个非闭包函数参数。与此相反, 标记“正确”的两个函数不回产生编译错误。这些函数调用不会违反限制, 因为 `外部(external)` 不是 `takesTwoFunctions(first:second:)` 里的一个参数。 +上述例子里的被标记为“错误”的四个函数调用会产生编译错误。因为参数 `first` 和 `second` 是非逃逸函数,它们不能够作为参数被传递到另一个非闭包函数。相对的, 标记“正确”的两个函数不会产生编译错误。这些函数调用不会违反限制,因为 `external` 不是 `takesTwoFunctions(first:second:)` 的参数之一。 -如果你需要避免这个限制, 标记其中之一的参数为逃逸, 或者使用 `withoutActuallyEscaping(_:do:)` 函数临时地转换非逃逸函数的其中一个参数为逃逸函数。关于避免内存访问冲突,可以参阅[内存安全](../chapter2/24_Memory_Safety.html)。 +如果你需要避免这个限制,标记其中之一的参数为逃逸,或者使用 `withoutActuallyEscaping(_:do:)` 函数临时地转换非逃逸函数的其中一个参数为逃逸函数。关于避免内存访问冲突,可以参阅[内存安全](../chapter2/24_Memory_Safety.html)。 > 函数类型语法 > -> *函数类型* → [*特性列表*](06_Attributes.html#attributes)可选 [*函数类型子句*](#function-type-argument-clause) **throws**可选 **->** [*类型*](#type) -> *函数类型* → [*特性列表*](06_Attributes.html#attributes)可选 [*函数类型子句*](#function-type-argument-clause) **rethrows­** **->** [*类型*](#type) +> *函数类型* → [*特性列表*](07_Attributes.html#attributes)可选 [*函数类型子句*](#function-type-argument-clause) **throws**可选 **->** [*类型*](#type) +> *函数类型* → [*特性列表*](07_Attributes.html#attributes)可选 [*函数类型子句*](#function-type-argument-clause) **rethrows­** **->** [*类型*](#type) -> *函数类型子句* → (­)­ -> *函数类型子句* → ([*函数类型参数列表*](#function-type-argument-list)*...*­可选)­ +> *函数类型子句* → **(**­ **)**­ +> *函数类型子句* → **(** [*函数类型参数列表*](#function-type-argument-list)*...*­可选 **)**­ -> *函数类型参数列表* → [*函数类型参数*](function-type-argument) | [*函数类型参数*](function-type-argument), [*函数类型参数列表*](#function-type-argument-list) +> *函数类型参数列表* → [*函数类型参数*](function-type-argument) | [*函数类型参数*](function-type-argument)**,** [*函数类型参数列表*](#function-type-argument-list) -> *函数类型参数* → [*特性列表*](06_Attributes.html#attributes)可选 **输入输出参数**可选 [*类型*](#type) | [*参数标签*](#argument-label) [*类型注解*](#type-annotation) +> *函数类型参数* → [*特性列表*](07_Attributes.html#attributes)可选 **输入输出参数**可选 [*类型*](#type) | [*参数标签*](#argument-label) [*类型注解*](#type-annotation) > *参数标签* → [*标识符*](02_Lexical_Structure.html#identifier) From c634f574a1d833d7b4ff05e8bd8b851471d09c8e Mon Sep 17 00:00:00 2001 From: edz Date: Wed, 27 Mar 2019 20:03:36 +0800 Subject: [PATCH 6/8] =?UTF-8?q?=E9=81=BF=E5=85=8D=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/26_Advanced_Operators.md | 468 ----------------------- 1 file changed, 468 deletions(-) delete mode 100644 source/chapter2/26_Advanced_Operators.md diff --git a/source/chapter2/26_Advanced_Operators.md b/source/chapter2/26_Advanced_Operators.md deleted file mode 100644 index 0464257f..00000000 --- a/source/chapter2/26_Advanced_Operators.md +++ /dev/null @@ -1,468 +0,0 @@ -# 高级运算符 - -除了之前介绍过的[基本运算符](./02_Basic_Operators.md),Swift 还提供了数种可以对数值进行复杂运算的高级运算符。它们包含了在 C 和 Objective-C 中已经被大家所熟知的位运算符和移位运算符。 - -与 C 语言中的算术运算符不同,Swift 中的算术运算符默认是不会溢出的。所有溢出行为都会被捕获并报告为错误。如果想让系统允许溢出行为,可以选择使用 Swift 中另一套默认支持溢出的运算符,比如溢出加法运算符(`&+`)。所有的这些溢出运算符都是以 `&` 开头的。 - -自定义结构体、类和枚举时,如果也为它们提供标准 Swift 运算符的实现,将会非常有用。在 Swift 中为这些运算符提供自定义的实现非常简单,运算符也会针对不同类型使用对应实现。 - -我们不用被预定义的运算符所限制。在 Swift 中可以自由地定义中缀、前缀、后缀和赋值运算符,它们具有自定义的优先级与关联值。这些运算符在代码中可以像预定义的运算符一样使用,你甚至可以扩展已有的类型以支持自定义运算符。 - -## 位运算符 {#bitwise_operators} - -*位运算符*可以操作数据结构中每个独立的比特位。它们通常被用在底层开发中,比如图形编程和创建设备驱动。位运算符在处理外部资源的原始数据时也十分有用,比如对自定义通信协议传输的数据进行编码和解码。 - -Swift 支持 C 语言中的全部位运算符,接下来会一一介绍。 - -### Bitwise NOT Operator(按位取反运算符) {#bitwise_not_operator} - -*按位取反运算符(`~`)*对一个数值的全部比特位进行取反: - -![Art/bitwiseNOT_2x.png](https://docs.swift.org/swift-book/_images/bitwiseNOT_2x.png) - -按位取反运算符是一个前缀运算符,直接放在运算数之前,并且它们之间不能添加任何空格: - -```swift -let initialBits: UInt8 = 0b00001111 -let invertedBits = ~initialBits // 等于 0b11110000 -``` - -`UInt8` 类型的整数有 8 个比特位,可以存储 `0 ~ 255` 之间的任意整数。这个例子初始化了一个 `UInt8` 类型的整数,并赋值为二进制的 `00001111`,它的前 4 位为 `0`,后 4 位为 `1`。这个值等价于十进制的 `15`。 - -接着使用按位取反运算符创建了一个名为 `invertedBits` 的常量,这个常量的值与全部位取反后的 `initialBits` 相等。即所有的 `0` 都变成了 `1`,同时所有的 `1` 都变成 `0`。`invertedBits` 的二进制值为 `11110000`,等价于无符号十进制数的 `240`。 - -### Bitwise AND Operator(按位与运算符) {#bitwise_and_operator} - -*按位与运算符(`&`)* 对两个数的比特位进行合并。它返回一个新的数,只有当两个数的对应位*都*为 `1` 的时候,新数的对应位才为 `1`: - -![Art/bitwiseAND_2x.png](https://docs.swift.org/swift-book/_images/bitwiseAND_2x.png) - -在下面的示例当中,`firstSixBits` 和 `lastSixBits` 中间 4 个位的值都为 `1`。使用按位与运算符之后,得到二进制数值 `00111100`,等价于无符号十进制数的 `60`: - -```swift -let firstSixBits: UInt8 = 0b11111100 -let lastSixBits: UInt8 = 0b00111111 -let middleFourBits = firstSixBits & lastSixBits // 等于 00111100 -``` - -### Bitwise OR Operator(按位或运算符) {#bitwise_or_operator} - -*按位或运算符(`|`)* 可以对两个数的比特位进行比较。它返回一个新的数,只要两个数的对应位中有*任意一个*为 `1` 时,新数的对应位就为 `1`: - -![Art/bitwiseOR_2x.png](https://docs.swift.org/swift-book/_images/bitwiseOR_2x.png) - -在下面的示例中,`someBits` 和 `moreBits` 存在不同的位被设置为 `1`。使用按位或运算符之后,得到二进制数值 `11111110`,等价于无符号十进制数的 `254`: - -```swift -let someBits: UInt8 = 0b10110010 -let moreBits: UInt8 = 0b01011110 -let combinedbits = someBits | moreBits // 等于 11111110 -``` - -### Bitwise XOR Operator(按位异或运算符) {#bitwise_xor_operator} - -*按位异或运算符*,或称“排外的或运算符”(`^`),可以对两个数的比特位进行比较。它返回一个新的数,当两个数的对应位不相同时,新数的对应位就为 `1`,并且对应位相同时则为 `0`: - -![Art/bitwiseXOR_2x.png](https://docs.swift.org/swift-book/_images/bitwiseXOR_2x.png) - -在下面的示例当中,`firstBits` 和 `otherBits` 都有一个自己为 `1`,而对方为 `0` 的位。按位异或运算符将新数的这两个位都设置为 `1`。在其余的位上 `firstBits` 和 `otherBits` 是相同的,所以设置为 `0`: - -```swift -let firstBits: UInt8 = 0b00010100 -let otherBits: UInt8 = 0b00000101 -let outputBits = firstBits ^ otherBits // 等于 00010001 -``` - -### Bitwise Left and Right Shift Operators(按位左移、右移运算符) {#bitwise_left_and_right_shift_operators} - -*按位左移运算符(`<<`)* 和 *按位右移运算符(`>>`)*可以对一个数的所有位进行指定位数的左移和右移,但是需要遵守下面定义的规则。 - -对一个数进行按位左移或按位右移,相当于对这个数进行乘以 2 或除以 2 的运算。将一个整数左移一位,等价于将这个数乘以 2,同样地,将一个整数右移一位,等价于将这个数除以 2。 - -#### 无符号整数的移位运算 {#shifting_behavior_for_unsigned_integers} - -对无符号整数进行移位的规则如下: - -1. 已存在的位按指定的位数进行左移和右移。 -2. 任何因移动而超出整型存储范围的位都会被丢弃。 -3. 用 `0` 来填充移位后产生的空白位。 - -这种方法称为*逻辑移位*。 - -以下这张图展示了 `11111111 << 1`(即把 `11111111` 向左移动 `1` 位),和 `11111111 >> 1`(即把 `11111111` 向右移动 `1` 位)的结果。蓝色的数字是被移位的,灰色的数字是被抛弃的,橙色的 `0` 则是被填充进来的: - -![Art/bitshiftUnsigned_2x.png](https://docs.swift.org/swift-book/_images/bitshiftUnsigned_2x.png) - -下面的代码演示了 Swift 中的移位运算: - -```swift -let shiftBits: UInt8 = 4 // 即二进制的 00000100 -shiftBits << 1 // 00001000 -shiftBits << 2 // 00010000 -shiftBits << 5 // 10000000 -shiftBits << 6 // 00000000 -shiftBits >> 2 // 00000001 -``` - -可以使用移位运算对其他的数据类型进行编码和解码: - -```swift -let pink: UInt32 = 0xCC6699 -let redComponent = (pink & 0xFF0000) >> 16 // redComponent 是 0xCC,即 204 -let greenComponent = (pink & 0x00FF00) >> 8 // greenComponent 是 0x66, 即 102 -let blueComponent = pink & 0x0000FF // blueComponent 是 0x99,即 153 -``` - -这个示例使用了一个命名为 `pink` 的 `UInt32` 型常量来存储 Cascading Style Sheets(CSS)中粉色的颜色值。该 CSS 的颜色值 `#CC6699`,在 Swift 中表示为十六进制的 `0xCC6699`。然后利用按位与运算符(`&`)和按位右移运算符(`>>`)从这个颜色值中分解出红(`CC`)、绿(`66`)以及蓝(`99`)三个部分。 - -红色部分是通过对 `0xCC6699` 和 `0xFF0000` 进行按位与运算后得到的。`0xFF0000` 中的 `0` 部分“掩盖”了 `OxCC6699` 中的第二、第三个字节,使得数值中的 `6699` 被忽略,只留下 `0xCC0000`。 - -然后,将这个数向右移动 16 位(`>> 16`)。十六进制中每两个字符占用 8 个比特位,所以移动 16 位后 `0xCC0000` 就变为 `0x0000CC`。这个数和 `0xCC` 是等同的,也就是十进制数值的 `204`。 - -同样的,绿色部分通过对 `0xCC6699` 和 `0x00FF00` 进行按位与运算得到 `0x006600`。然后将这个数向右移动 8 位,得到 `0x66`,也就是十进制数值的 `102`。 - -最后,蓝色部分通过对 `0xCC6699` 和 `0x0000FF` 进行按位与运算得到 `0x000099`。这里不需要再向右移位,而 `0x000099` 也就是 `0x99` ,也就是十进制数值的 `153`。 - -#### 有符号整数的移位运算 {#shifting_behavior_for_signed_integers} - -对比无符号整数,有符号整数的移位运算相对复杂得多,这种复杂性源于有符号整数的二进制表现形式。(为了简单起见,以下的示例都是基于 8 比特的有符号整数,但是其中的原理对任何位数的有符号整数都是通用的。) - -有符号整数使用第 1 个比特位(通常被称为*符号位*)来表示这个数的正负。符号位为 `0` 代表正数,为 `1` 代表负数。 - -其余的比特位(通常被称为*数值位*)存储了实际的值。有符号正整数和无符号数的存储方式是一样的,都是从 `0` 开始算起。这是值为 `4` 的 `Int8` 型整数的二进制位表现形式: - -![Art/bitshiftSignedFour_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSignedFour_2x.png) - -符号位为 `0`(代表这是一个“正数”),另外 7 位则代表了十进制数值 `4` 的二进制表示。 - -负数的存储方式略有不同。它存储 `2` 的 `n` 次方减去其实际值的绝对值,这里的 `n` 是数值位的位数。一个 8 比特位的数有 7 个比特位是数值位,所以是 `2` 的 `7` 次方,即 `128`。 - -这是值为 `-4` 的 `Int8` 型整数的二进制表现形式: - -![Art/bitshiftSignedMinusFour_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSignedMinusFour_2x.png) - -这次的符号位为 `1`,说明这是一个负数,另外 7 个位则代表了数值 `124`(即 `128 - 4`)的二进制表示: - -![Art/bitshiftSignedMinusFourValue_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSignedMinusFourValue_2x.png) - -负数的表示通常被称为*二进制补码*。用这种方法来表示负数乍看起来有点奇怪,但它有几个优点。 - -首先,如果想对 `-1` 和 `-4` 进行加法运算,我们只需要对这两个数的全部 8 个比特位执行标准的二进制相加(包括符号位),并且将计算结果中超出 8 位的数值丢弃: - -![Art/bitshiftSignedAddition_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSignedAddition_2x.png) - -其次,使用二进制补码可以使负数的按位左移和右移运算得到跟正数同样的效果,即每向左移一位就将自身的数值乘以 2,每向右一位就将自身的数值除以 2。要达到此目的,对有符号整数的右移有一个额外的规则:当对有符号整数进行按位右移运算时,遵循与无符号整数相同的规则,但是对于移位产生的空白位使用*符号位*进行填充,而不是用 `0`。 - -![Art/bitshiftSigned_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSigned_2x.png) - -这个行为可以确保有符号整数的符号位不会因为右移运算而改变,这通常被称为*算术移位*。 - -由于正数和负数的特殊存储方式,在对它们进行右移的时候,会使它们越来越接近 `0`。在移位的过程中保持符号位不变,意味着负整数在接近 `0` 的过程中会一直保持为负。 - -## 溢出运算符 {#overflow_operators} - -当向一个整数类型的常量或者变量赋予超过它容量的值时,Swift 默认会报错,而不是允许生成一个无效的数。这个行为为我们在运算过大或者过小的数时提供了额外的安全性。 - -例如,`Int16` 型整数能容纳的有符号整数范围是 `-32768` 到 `32767`。当为一个 `Int16` 类型的变量或常量赋予的值超过这个范围时,系统就会报错: - -```swift -var potentialOverflow = Int16.max -// potentialOverflow 的值是 32767,这是 Int16 能容纳的最大整数 -potentialOverflow += 1 -// 这里会报错 -``` - -在赋值时为过大或者过小的情况提供错误处理,能让我们在处理边界值时更加灵活。 - -然而,当你希望的时候也可以选择让系统在数值溢出的时候采取截断处理,而非报错。Swift 提供的三个*溢出运算符*来让系统支持整数溢出运算。这些运算符都是以 `&` 开头的: - -* 溢出加法 `&+` -* 溢出减法 `&-` -* 溢出乘法 `&*` - -### 数值溢出 {#value_overflow} - -数值有可能出现上溢或者下溢。 - -这个示例演示了当我们对一个无符号整数使用溢出加法(`&+`)进行上溢运算时会发生什么: - -```swift -var unsignedOverflow = UInt8.max -// unsignedOverflow 等于 UInt8 所能容纳的最大整数 255 -unsignedOverflow = unsignedOverflow &+ 1 -// 此时 unsignedOverflow 等于 0 -``` - -`unsignedOverflow` 被初始化为 `UInt8` 所能容纳的最大整数(`255`,以二进制表示即 `11111111`)。然后使用溢出加法运算符(`&+`)对其进行加 `1` 运算。这使得它的二进制表示正好超出 `UInt8` 所能容纳的位数,也就导致了数值的溢出,如下图所示。数值溢出后,仍然留在 `UInt8` 边界内的值是 `00000000`,也就是十进制数值的 `0`。 - -![Art/overflowAddition_2x.png](https://docs.swift.org/swift-book/_images/overflowAddition_2x.png) - -当允许对一个无符号整数进行下溢运算时也会产生类似的情况。这里有一个使用溢出减法运算符(`&-`)的例子: - -```swift -var unsignedOverflow = UInt8.min -// unsignedOverflow 等于 UInt8 所能容纳的最小整数 0 -unsignedOverflow = unsignedOverflow &- 1 -// 此时 unsignedOverflow 等于 255 -``` - -`UInt8` 型整数能容纳的最小值是 `0`,以二进制表示即 `00000000`。当使用溢出减法运算符对其进行减 `1` 运算时,数值会产生下溢并被截断为 `11111111`, 也就是十进制数值的 `255`。 - -![Art/overflowUnsignedSubtraction_2x.png](https://docs.swift.org/swift-book/_images/overflowUnsignedSubtraction_2x.png) - -溢出也会发生在有符号整型上。针对有符号整型的所有溢出加法或者减法运算都是按位运算的方式执行的,符号位也需要参与计算,正如[按位左移、右移运算符](#bitwise_left_and_right_shift_operators)所描述的。 - -```swift -var signedOverflow = Int8.min -// signedOverflow 等于 Int8 所能容纳的最小整数 -128 -signedOverflow = signedOverflow &- 1 -// 此时 signedOverflow 等于 127 -``` - -`Int8` 型整数能容纳的最小值是 `-128`,以二进制表示即 `10000000`。当使用溢出减法运算符对其进行减 `1` 运算时,符号位被翻转,得到二进制数值 `01111111`,也就是十进制数值的 `127`,这个值也是 `Int8` 型整所能容纳的最大值。 - -![Art/overflowSignedSubtraction_2x.png](https://docs.swift.org/swift-book/_images/overflowSignedSubtraction_2x.png) - -对于无符号与有符号整型数值来说,当出现上溢时,它们会从数值所能容纳的最大数变成最小数。同样地,当发生下溢时,它们会从所能容纳的最小数变成最大数。 - -## 优先级和结合性 {#precedence_and_associativity} - -运算符的*优先级*使得一些运算符优先于其他运算符;它们会先被执行。 - -*结合性*定义了相同优先级的运算符是如何结合的,也就是说,是与左边结合为一组,还是与右边结合为一组。可以将其理解为“它们是与左边的表达式结合的”,或者“它们是与右边的表达式结合的”。 - -当考虑一个复合表达式的计算顺序时,运算符的优先级和结合性是非常重要的。举例来说,运算符优先级解释了为什么下面这个表达式的运算结果会是 `17`。 - -```swift -2 + 3 % 4 * 5 -// 结果是 17 -``` - -如果你直接从左到右进行运算,你可能认为运算的过程是这样的: - -- 2 + 3 = 5 -- 5 % 4 = 1 -- 1 * 5 = 5 - -但是正确答案是 `17` 而不是 `5`。优先级高的运算符要先于优先级低的运算符进行计算。与 C 语言类似,在 Swift 中,乘法运算符(`*`)与取余运算符(`%`)的优先级高于加法运算符(`+`)。因此,它们的计算顺序要先于加法运算。 - -而乘法运算与取余运算的优先级*相同*。这时为了得到正确的运算顺序,还需要考虑结合性。乘法运算与取余运算都是左结合的。可以将这考虑成,从它们的左边开始为这两部分表达式都隐式地加上括号: - -```swift -2 + ((3 % 4) * 5) -``` - -`(3 % 4)` 等于 `3`,所以表达式相当于: - -```swift -2 + (3 * 5) -``` - -`3 * 5` 等于 `15`,所以表达式相当于: - -```swift -2 + 15 -``` - -因此计算结果为 `17`。 - -有关 Swift 标准库提供的操作符信息,包括操作符优先级组和结核性设置的完整列表,请参见[操作符声明](https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations)。 - -> 注意 -> -> 相对 C 语言和 Objective-C 来说,Swift 的运算符优先级和结合性规则更加简洁和可预测。但是,这也意味着它们相较于 C 语言及其衍生语言并不是完全一致。在对现有的代码进行移植的时候,要注意确保运算符的行为仍然符合你的预期。 - -## 运算符函数 {#operator_functions} - -类和结构体可以为现有的运算符提供自定义的实现。这通常被称为运算符*重载*。 - -下面的例子展示了如何让自定义的结构体支持加法运算符(`+`)。算术加法运算符是一个*二元运算符*,因为它是对两个值进行运算,同时它还可以称为*中缀*运算符,因为它出现在两个值中间。 - -例子中定义了一个名为 `Vector2D` 的结构体用来表示二维坐标向量 `(x, y)`,紧接着定义了一个可以将两个 `Vector2D` 结构体实例进行相加的*运算符函数*: - -```swift -struct Vector2D { - var x = 0.0, y = 0.0 -} - -extension Vector2D { - static func + (left: Vector2D, right: Vector2D) -> Vector2D { - return Vector2D(x: left.x + right.x, y: left.y + right.y) - } -} -``` - -该运算符函数被定义为 `Vector2D` 上的一个类方法,并且函数的名字与它要进行重载的 `+` 名字一致。因为加法运算并不是一个向量必需的功能,所以这个类方法被定义在 `Vector2D` 的一个扩展中,而不是 `Vector2D` 结构体声明内。而算术加法运算符是二元运算符,所以这个运算符函数接收两个类型为 `Vector2D` 的参数,同时有一个 `Vector2D` 类型的返回值。 - -在这个实现中,输入参数分别被命名为 `left` 和 `right`,代表在 `+` 运算符左边和右边的两个 `Vector2D` 实例。函数返回了一个新的 `Vector2D` 实例,这个实例的 `x` 和 `y` 分别等于作为参数的两个实例的 `x` 和 `y` 的值之和。 - -这个类方法可以在任意两个 `Vector2D` 实例中间作为中缀运算符来使用: - -```swift -let vector = Vector2D(x: 3.0, y: 1.0) -let anotherVector = Vector2D(x: 2.0, y: 4.0) -let combinedVector = vector + anotherVector -// combinedVector 是一个新的 Vector2D 实例,值为 (5.0, 5.0) -``` - -这个例子实现两个向量 `(3.0,1.0)` 和 `(2.0,4.0)` 的相加,并得到新的向量 `(5.0,5.0)`。这个过程如下图示: - -![Art/vectorAddition_2x.png](https://docs.swift.org/swift-book/_images/vectorAddition_2x.png) - -### 前缀和后缀运算符 {#prefix_and_postfix_operators} - -上个例子演示了一个二元中缀运算符的自定义实现。类与结构体也能提供标准*一元运算符*的实现。一元运算符只运算一个值。当运算符出现在值之前时,它就是*前缀*的(例如 `-a`),而当它出现在值之后时,它就是*后缀*的(例如 `b!`)。 - -要实现前缀或者后缀运算符,需要在声明运算符函数的时候在 `func` 关键字之前指定 `prefix` 或者 `postfix` 修饰符: - -```swift -extension Vector2D { - static prefix func - (vector: Vector2D) -> Vector2D { - return Vector2D(x: -vector.x, y: -vector.y) - } -} -``` - -这段代码为 `Vector2D` 类型实现了一元运算符(`-a`)。由于该运算符是前缀运算符,所以这个函数需要加上 `prefix` 修饰符。 - -对于简单数值,一元负号运算符可以对它们的正负性进行改变。对于 `Vector2D` 来说,该运算将其 `x` 和 `y` 属性的正负性都进行了改变: - -```swift -let positive = Vector2D(x: 3.0, y: 4.0) -let negative = -positive -// negative 是一个值为 (-3.0, -4.0) 的 Vector2D 实例 -let alsoPositive = -negative -// alsoPositive 是一个值为 (3.0, 4.0) 的 Vector2D 实例 -``` - -### 复合赋值运算符 {#compound_assignment_operators} - -*复合赋值运算符*将赋值运算符(`=`)与其它运算符进行结合。例如,将加法与赋值结合成加法赋值运算符(`+=`)。在实现的时候,需要把运算符的左参数设置成 `inout` 类型,因为这个参数的值会在运算符函数内直接被修改。 - -在下面的例子中,对 `Vector2D` 实例实现了一个加法赋值运算符函数: - -在下面的例子中,对 `Vector2D` 实例实现了一个加法赋值运算符函数: - -```swift -extension Vector2D { - static func += (left: inout Vector2D, right: Vector2D) { - left = left + right - } -} -``` - -因为加法运算在之前已经定义过了,所以在这里无需重新定义。在这里可以直接利用现有的加法运算符函数,用它来对左值和右值进行相加,并再次赋值给左值: - -```swift -var original = Vector2D(x: 1.0, y: 2.0) -let vectorToAdd = Vector2D(x: 3.0, y: 4.0) -original += vectorToAdd -// original 的值现在为 (4.0, 6.0) -``` - -> 注意 -> -> 不能对默认的赋值运算符(`=`)进行重载。只有复合赋值运算符可以被重载。同样地,也无法对三元条件运算符 (`a ? b : c`) 进行重载。 - -### 等价运算符 {#equivalence_operators} - -通常情况下,自定义的类和结构体没有对*等价运算符*进行默认实现,等价运算符通常被称为*相等*运算符(`==`)与*不等*运算符(`!=`)。 - -为了使用等价运算符对自定义的类型进行判等运算,需要为“相等”运算符提供自定义实现,实现的方法与其它中缀运算符一样, 并且增加对标准库 `Equatable` 协议的遵循: - -```swift -extension Vector2D: Equatable { - static func == (left: Vector2D, right: Vector2D) -> Bool { - return (left.x == right.x) && (left.y == right.y) - } -} -``` - -上述代码实现了“相等”运算符(`==`)来判断两个 `Vector2D` 实例是否相等。对于 `Vector2D` 来说,“相等”意味着“两个实例的 `x` 和 `y` 都相等”,这也是代码中用来进行判等的逻辑。如果你已经实现了“相等”运算符,通常情况下你并不需要自己再去实现“不等”运算符(`!=`)。标准库对于“不等”运算符提供了默认的实现,它简单地将“相等”运算符的结果进行取反后返回。 - -现在我们可以使用这两个运算符来判断两个 `Vector2D` 实例是否相等: - -```swift -let twoThree = Vector2D(x: 2.0, y: 3.0) -let anotherTwoThree = Vector2D(x: 2.0, y: 3.0) -if twoThree == anotherTwoThree { - print("These two vectors are equivalent.") -} -// 打印“These two vectors are equivalent.” -``` - -Swift 为以下数种自定义类型提供等价运算符的默认实现: - -- 只拥有存储属性,并且它们全都遵循 `Equatable` 协议的结构体 -- 只拥有关联类型,并且它们全都遵循 `Equatable` 协议的枚举 -- 没有关联类型的枚举 - -在类型原始的声明中声明遵循 `Equatable` 来接收这些默认实现。 - -下面为三维位置向量 `(x, y, z)` 定义的 `Vector3D` 结构体,与 `Vector2D` 类似。由于 `x`,`y` 和 `z` 属性都是 `Equatable` 类型,`Vector3D` 获得了默认的等价运算符实现。 - -```swift -struct Vector3D: Equatable { - var x = 0.0, y = 0.0, z = 0.0 -} - -let twoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0) -let anotherTwoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0) -if twoThreeFour == anotherTwoThreeFour { - print("These two vectors are also equivalent.") -} -// 打印“These two vectors are also equivalent.” -``` - -## 自定义运算符 {#custom_operators} - -除了实现标准运算符,在 Swift 中还可以声明和实现*自定义运算符*。可以用来自定义运算符的字符列表请参考[运算符](../chapter3/02_Lexical_Structure.html#operators)。 - -新的运算符要使用 `operator` 关键字在全局作用域内进行定义,同时还要指定 `prefix`、`infix` 或者 `postfix` 修饰符: - -```swift -prefix operator +++ -``` - -上面的代码定义了一个新的名为 `+++` 的前缀运算符。对于这个运算符,在 Swift 中并没有已知的意义,因此在针对 `Vector2D` 实例的特定上下文中,给予了它自定义的意义。对这个示例来讲,`+++` 被实现为“前缀双自增”运算符。它使用了前面定义的复合加法运算符来让矩阵与自身进行相加,从而让 `Vector2D` 实例的 `x` 属性和 `y` 属性值翻倍。你可以像下面这样通过对 `Vector2D` 添加一个 `+++` 类方法,来实现 `+++` 运算符: - -```swift -extension Vector2D { - static prefix func +++ (vector: inout Vector2D) -> Vector2D { - vector += vector - return vector - } -} - -var toBeDoubled = Vector2D(x: 1.0, y: 4.0) -let afterDoubling = +++toBeDoubled -// toBeDoubled 现在的值为 (2.0, 8.0) -// afterDoubling 现在的值也为 (2.0, 8.0) -``` - -### 自定义中缀运算符的优先级 {#precedence_and_associativity_for_custom_infix_operators} - -每个自定义中缀运算符都属于某个优先级组。优先级组指定了这个运算符相对于其他中缀运算符的优先级和结合性。[优先级和结合性](#precedence_and_associativity)中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。 - -而没有明确放入某个优先级组的自定义中缀运算符将会被放到一个默认的优先级组内,其优先级高于三元运算符。 - -以下例子定义了一个新的自定义中缀运算符 `+-`,此运算符属于 `AdditionPrecedence` 优先组: - -```swift -infix operator +-: AdditionPrecedence -extension Vector2D { - static func +- (left: Vector2D, right: Vector2D) -> Vector2D { - return Vector2D(x: left.x + right.x, y: left.y - right.y) - } -} -let firstVector = Vector2D(x: 1.0, y: 2.0) -let secondVector = Vector2D(x: 3.0, y: 4.0) -let plusMinusVector = firstVector +- secondVector -// plusMinusVector 是一个 Vector2D 实例,并且它的值为 (4.0, -2.0) -``` - -这个运算符把两个向量的 `x` 值相加,同时从第一个向量的 `y` 中减去第二个向量的 `y` 。因为它本质上是属于“相加型”运算符,所以将它放置在 `+` 和 `-` 等默认中缀“相加型”运算符相同的优先级组中。关于 Swift 标准库提供的运算符,以及完整的运算符优先级组和结合性设置,请参考 [运算符声明](https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations)。而更多关于优先级组以及自定义操作符和优先级组的语法,请参考[运算符声明](https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID380)。 - -> 注意 -> -> 当定义前缀与后缀运算符的时候,我们并没有指定优先级。然而,如果对同一个值同时使用前缀与后缀运算符,则后缀运算符会先参与运算。 \ No newline at end of file From 93cffd5ff14126f14d6b856988379091fa423908 Mon Sep 17 00:00:00 2001 From: edz Date: Wed, 27 Mar 2019 20:08:28 +0800 Subject: [PATCH 7/8] =?UTF-8?q?=E9=81=BF=E5=85=8D=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/26_Advanced_Operators.md | 468 +++++++++++++++++++++++ 1 file changed, 468 insertions(+) create mode 100644 source/chapter2/26_Advanced_Operators.md diff --git a/source/chapter2/26_Advanced_Operators.md b/source/chapter2/26_Advanced_Operators.md new file mode 100644 index 00000000..0464257f --- /dev/null +++ b/source/chapter2/26_Advanced_Operators.md @@ -0,0 +1,468 @@ +# 高级运算符 + +除了之前介绍过的[基本运算符](./02_Basic_Operators.md),Swift 还提供了数种可以对数值进行复杂运算的高级运算符。它们包含了在 C 和 Objective-C 中已经被大家所熟知的位运算符和移位运算符。 + +与 C 语言中的算术运算符不同,Swift 中的算术运算符默认是不会溢出的。所有溢出行为都会被捕获并报告为错误。如果想让系统允许溢出行为,可以选择使用 Swift 中另一套默认支持溢出的运算符,比如溢出加法运算符(`&+`)。所有的这些溢出运算符都是以 `&` 开头的。 + +自定义结构体、类和枚举时,如果也为它们提供标准 Swift 运算符的实现,将会非常有用。在 Swift 中为这些运算符提供自定义的实现非常简单,运算符也会针对不同类型使用对应实现。 + +我们不用被预定义的运算符所限制。在 Swift 中可以自由地定义中缀、前缀、后缀和赋值运算符,它们具有自定义的优先级与关联值。这些运算符在代码中可以像预定义的运算符一样使用,你甚至可以扩展已有的类型以支持自定义运算符。 + +## 位运算符 {#bitwise_operators} + +*位运算符*可以操作数据结构中每个独立的比特位。它们通常被用在底层开发中,比如图形编程和创建设备驱动。位运算符在处理外部资源的原始数据时也十分有用,比如对自定义通信协议传输的数据进行编码和解码。 + +Swift 支持 C 语言中的全部位运算符,接下来会一一介绍。 + +### Bitwise NOT Operator(按位取反运算符) {#bitwise_not_operator} + +*按位取反运算符(`~`)*对一个数值的全部比特位进行取反: + +![Art/bitwiseNOT_2x.png](https://docs.swift.org/swift-book/_images/bitwiseNOT_2x.png) + +按位取反运算符是一个前缀运算符,直接放在运算数之前,并且它们之间不能添加任何空格: + +```swift +let initialBits: UInt8 = 0b00001111 +let invertedBits = ~initialBits // 等于 0b11110000 +``` + +`UInt8` 类型的整数有 8 个比特位,可以存储 `0 ~ 255` 之间的任意整数。这个例子初始化了一个 `UInt8` 类型的整数,并赋值为二进制的 `00001111`,它的前 4 位为 `0`,后 4 位为 `1`。这个值等价于十进制的 `15`。 + +接着使用按位取反运算符创建了一个名为 `invertedBits` 的常量,这个常量的值与全部位取反后的 `initialBits` 相等。即所有的 `0` 都变成了 `1`,同时所有的 `1` 都变成 `0`。`invertedBits` 的二进制值为 `11110000`,等价于无符号十进制数的 `240`。 + +### Bitwise AND Operator(按位与运算符) {#bitwise_and_operator} + +*按位与运算符(`&`)* 对两个数的比特位进行合并。它返回一个新的数,只有当两个数的对应位*都*为 `1` 的时候,新数的对应位才为 `1`: + +![Art/bitwiseAND_2x.png](https://docs.swift.org/swift-book/_images/bitwiseAND_2x.png) + +在下面的示例当中,`firstSixBits` 和 `lastSixBits` 中间 4 个位的值都为 `1`。使用按位与运算符之后,得到二进制数值 `00111100`,等价于无符号十进制数的 `60`: + +```swift +let firstSixBits: UInt8 = 0b11111100 +let lastSixBits: UInt8 = 0b00111111 +let middleFourBits = firstSixBits & lastSixBits // 等于 00111100 +``` + +### Bitwise OR Operator(按位或运算符) {#bitwise_or_operator} + +*按位或运算符(`|`)* 可以对两个数的比特位进行比较。它返回一个新的数,只要两个数的对应位中有*任意一个*为 `1` 时,新数的对应位就为 `1`: + +![Art/bitwiseOR_2x.png](https://docs.swift.org/swift-book/_images/bitwiseOR_2x.png) + +在下面的示例中,`someBits` 和 `moreBits` 存在不同的位被设置为 `1`。使用按位或运算符之后,得到二进制数值 `11111110`,等价于无符号十进制数的 `254`: + +```swift +let someBits: UInt8 = 0b10110010 +let moreBits: UInt8 = 0b01011110 +let combinedbits = someBits | moreBits // 等于 11111110 +``` + +### Bitwise XOR Operator(按位异或运算符) {#bitwise_xor_operator} + +*按位异或运算符*,或称“排外的或运算符”(`^`),可以对两个数的比特位进行比较。它返回一个新的数,当两个数的对应位不相同时,新数的对应位就为 `1`,并且对应位相同时则为 `0`: + +![Art/bitwiseXOR_2x.png](https://docs.swift.org/swift-book/_images/bitwiseXOR_2x.png) + +在下面的示例当中,`firstBits` 和 `otherBits` 都有一个自己为 `1`,而对方为 `0` 的位。按位异或运算符将新数的这两个位都设置为 `1`。在其余的位上 `firstBits` 和 `otherBits` 是相同的,所以设置为 `0`: + +```swift +let firstBits: UInt8 = 0b00010100 +let otherBits: UInt8 = 0b00000101 +let outputBits = firstBits ^ otherBits // 等于 00010001 +``` + +### Bitwise Left and Right Shift Operators(按位左移、右移运算符) {#bitwise_left_and_right_shift_operators} + +*按位左移运算符(`<<`)* 和 *按位右移运算符(`>>`)*可以对一个数的所有位进行指定位数的左移和右移,但是需要遵守下面定义的规则。 + +对一个数进行按位左移或按位右移,相当于对这个数进行乘以 2 或除以 2 的运算。将一个整数左移一位,等价于将这个数乘以 2,同样地,将一个整数右移一位,等价于将这个数除以 2。 + +#### 无符号整数的移位运算 {#shifting_behavior_for_unsigned_integers} + +对无符号整数进行移位的规则如下: + +1. 已存在的位按指定的位数进行左移和右移。 +2. 任何因移动而超出整型存储范围的位都会被丢弃。 +3. 用 `0` 来填充移位后产生的空白位。 + +这种方法称为*逻辑移位*。 + +以下这张图展示了 `11111111 << 1`(即把 `11111111` 向左移动 `1` 位),和 `11111111 >> 1`(即把 `11111111` 向右移动 `1` 位)的结果。蓝色的数字是被移位的,灰色的数字是被抛弃的,橙色的 `0` 则是被填充进来的: + +![Art/bitshiftUnsigned_2x.png](https://docs.swift.org/swift-book/_images/bitshiftUnsigned_2x.png) + +下面的代码演示了 Swift 中的移位运算: + +```swift +let shiftBits: UInt8 = 4 // 即二进制的 00000100 +shiftBits << 1 // 00001000 +shiftBits << 2 // 00010000 +shiftBits << 5 // 10000000 +shiftBits << 6 // 00000000 +shiftBits >> 2 // 00000001 +``` + +可以使用移位运算对其他的数据类型进行编码和解码: + +```swift +let pink: UInt32 = 0xCC6699 +let redComponent = (pink & 0xFF0000) >> 16 // redComponent 是 0xCC,即 204 +let greenComponent = (pink & 0x00FF00) >> 8 // greenComponent 是 0x66, 即 102 +let blueComponent = pink & 0x0000FF // blueComponent 是 0x99,即 153 +``` + +这个示例使用了一个命名为 `pink` 的 `UInt32` 型常量来存储 Cascading Style Sheets(CSS)中粉色的颜色值。该 CSS 的颜色值 `#CC6699`,在 Swift 中表示为十六进制的 `0xCC6699`。然后利用按位与运算符(`&`)和按位右移运算符(`>>`)从这个颜色值中分解出红(`CC`)、绿(`66`)以及蓝(`99`)三个部分。 + +红色部分是通过对 `0xCC6699` 和 `0xFF0000` 进行按位与运算后得到的。`0xFF0000` 中的 `0` 部分“掩盖”了 `OxCC6699` 中的第二、第三个字节,使得数值中的 `6699` 被忽略,只留下 `0xCC0000`。 + +然后,将这个数向右移动 16 位(`>> 16`)。十六进制中每两个字符占用 8 个比特位,所以移动 16 位后 `0xCC0000` 就变为 `0x0000CC`。这个数和 `0xCC` 是等同的,也就是十进制数值的 `204`。 + +同样的,绿色部分通过对 `0xCC6699` 和 `0x00FF00` 进行按位与运算得到 `0x006600`。然后将这个数向右移动 8 位,得到 `0x66`,也就是十进制数值的 `102`。 + +最后,蓝色部分通过对 `0xCC6699` 和 `0x0000FF` 进行按位与运算得到 `0x000099`。这里不需要再向右移位,而 `0x000099` 也就是 `0x99` ,也就是十进制数值的 `153`。 + +#### 有符号整数的移位运算 {#shifting_behavior_for_signed_integers} + +对比无符号整数,有符号整数的移位运算相对复杂得多,这种复杂性源于有符号整数的二进制表现形式。(为了简单起见,以下的示例都是基于 8 比特的有符号整数,但是其中的原理对任何位数的有符号整数都是通用的。) + +有符号整数使用第 1 个比特位(通常被称为*符号位*)来表示这个数的正负。符号位为 `0` 代表正数,为 `1` 代表负数。 + +其余的比特位(通常被称为*数值位*)存储了实际的值。有符号正整数和无符号数的存储方式是一样的,都是从 `0` 开始算起。这是值为 `4` 的 `Int8` 型整数的二进制位表现形式: + +![Art/bitshiftSignedFour_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSignedFour_2x.png) + +符号位为 `0`(代表这是一个“正数”),另外 7 位则代表了十进制数值 `4` 的二进制表示。 + +负数的存储方式略有不同。它存储 `2` 的 `n` 次方减去其实际值的绝对值,这里的 `n` 是数值位的位数。一个 8 比特位的数有 7 个比特位是数值位,所以是 `2` 的 `7` 次方,即 `128`。 + +这是值为 `-4` 的 `Int8` 型整数的二进制表现形式: + +![Art/bitshiftSignedMinusFour_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSignedMinusFour_2x.png) + +这次的符号位为 `1`,说明这是一个负数,另外 7 个位则代表了数值 `124`(即 `128 - 4`)的二进制表示: + +![Art/bitshiftSignedMinusFourValue_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSignedMinusFourValue_2x.png) + +负数的表示通常被称为*二进制补码*。用这种方法来表示负数乍看起来有点奇怪,但它有几个优点。 + +首先,如果想对 `-1` 和 `-4` 进行加法运算,我们只需要对这两个数的全部 8 个比特位执行标准的二进制相加(包括符号位),并且将计算结果中超出 8 位的数值丢弃: + +![Art/bitshiftSignedAddition_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSignedAddition_2x.png) + +其次,使用二进制补码可以使负数的按位左移和右移运算得到跟正数同样的效果,即每向左移一位就将自身的数值乘以 2,每向右一位就将自身的数值除以 2。要达到此目的,对有符号整数的右移有一个额外的规则:当对有符号整数进行按位右移运算时,遵循与无符号整数相同的规则,但是对于移位产生的空白位使用*符号位*进行填充,而不是用 `0`。 + +![Art/bitshiftSigned_2x.png](https://docs.swift.org/swift-book/_images/bitshiftSigned_2x.png) + +这个行为可以确保有符号整数的符号位不会因为右移运算而改变,这通常被称为*算术移位*。 + +由于正数和负数的特殊存储方式,在对它们进行右移的时候,会使它们越来越接近 `0`。在移位的过程中保持符号位不变,意味着负整数在接近 `0` 的过程中会一直保持为负。 + +## 溢出运算符 {#overflow_operators} + +当向一个整数类型的常量或者变量赋予超过它容量的值时,Swift 默认会报错,而不是允许生成一个无效的数。这个行为为我们在运算过大或者过小的数时提供了额外的安全性。 + +例如,`Int16` 型整数能容纳的有符号整数范围是 `-32768` 到 `32767`。当为一个 `Int16` 类型的变量或常量赋予的值超过这个范围时,系统就会报错: + +```swift +var potentialOverflow = Int16.max +// potentialOverflow 的值是 32767,这是 Int16 能容纳的最大整数 +potentialOverflow += 1 +// 这里会报错 +``` + +在赋值时为过大或者过小的情况提供错误处理,能让我们在处理边界值时更加灵活。 + +然而,当你希望的时候也可以选择让系统在数值溢出的时候采取截断处理,而非报错。Swift 提供的三个*溢出运算符*来让系统支持整数溢出运算。这些运算符都是以 `&` 开头的: + +* 溢出加法 `&+` +* 溢出减法 `&-` +* 溢出乘法 `&*` + +### 数值溢出 {#value_overflow} + +数值有可能出现上溢或者下溢。 + +这个示例演示了当我们对一个无符号整数使用溢出加法(`&+`)进行上溢运算时会发生什么: + +```swift +var unsignedOverflow = UInt8.max +// unsignedOverflow 等于 UInt8 所能容纳的最大整数 255 +unsignedOverflow = unsignedOverflow &+ 1 +// 此时 unsignedOverflow 等于 0 +``` + +`unsignedOverflow` 被初始化为 `UInt8` 所能容纳的最大整数(`255`,以二进制表示即 `11111111`)。然后使用溢出加法运算符(`&+`)对其进行加 `1` 运算。这使得它的二进制表示正好超出 `UInt8` 所能容纳的位数,也就导致了数值的溢出,如下图所示。数值溢出后,仍然留在 `UInt8` 边界内的值是 `00000000`,也就是十进制数值的 `0`。 + +![Art/overflowAddition_2x.png](https://docs.swift.org/swift-book/_images/overflowAddition_2x.png) + +当允许对一个无符号整数进行下溢运算时也会产生类似的情况。这里有一个使用溢出减法运算符(`&-`)的例子: + +```swift +var unsignedOverflow = UInt8.min +// unsignedOverflow 等于 UInt8 所能容纳的最小整数 0 +unsignedOverflow = unsignedOverflow &- 1 +// 此时 unsignedOverflow 等于 255 +``` + +`UInt8` 型整数能容纳的最小值是 `0`,以二进制表示即 `00000000`。当使用溢出减法运算符对其进行减 `1` 运算时,数值会产生下溢并被截断为 `11111111`, 也就是十进制数值的 `255`。 + +![Art/overflowUnsignedSubtraction_2x.png](https://docs.swift.org/swift-book/_images/overflowUnsignedSubtraction_2x.png) + +溢出也会发生在有符号整型上。针对有符号整型的所有溢出加法或者减法运算都是按位运算的方式执行的,符号位也需要参与计算,正如[按位左移、右移运算符](#bitwise_left_and_right_shift_operators)所描述的。 + +```swift +var signedOverflow = Int8.min +// signedOverflow 等于 Int8 所能容纳的最小整数 -128 +signedOverflow = signedOverflow &- 1 +// 此时 signedOverflow 等于 127 +``` + +`Int8` 型整数能容纳的最小值是 `-128`,以二进制表示即 `10000000`。当使用溢出减法运算符对其进行减 `1` 运算时,符号位被翻转,得到二进制数值 `01111111`,也就是十进制数值的 `127`,这个值也是 `Int8` 型整所能容纳的最大值。 + +![Art/overflowSignedSubtraction_2x.png](https://docs.swift.org/swift-book/_images/overflowSignedSubtraction_2x.png) + +对于无符号与有符号整型数值来说,当出现上溢时,它们会从数值所能容纳的最大数变成最小数。同样地,当发生下溢时,它们会从所能容纳的最小数变成最大数。 + +## 优先级和结合性 {#precedence_and_associativity} + +运算符的*优先级*使得一些运算符优先于其他运算符;它们会先被执行。 + +*结合性*定义了相同优先级的运算符是如何结合的,也就是说,是与左边结合为一组,还是与右边结合为一组。可以将其理解为“它们是与左边的表达式结合的”,或者“它们是与右边的表达式结合的”。 + +当考虑一个复合表达式的计算顺序时,运算符的优先级和结合性是非常重要的。举例来说,运算符优先级解释了为什么下面这个表达式的运算结果会是 `17`。 + +```swift +2 + 3 % 4 * 5 +// 结果是 17 +``` + +如果你直接从左到右进行运算,你可能认为运算的过程是这样的: + +- 2 + 3 = 5 +- 5 % 4 = 1 +- 1 * 5 = 5 + +但是正确答案是 `17` 而不是 `5`。优先级高的运算符要先于优先级低的运算符进行计算。与 C 语言类似,在 Swift 中,乘法运算符(`*`)与取余运算符(`%`)的优先级高于加法运算符(`+`)。因此,它们的计算顺序要先于加法运算。 + +而乘法运算与取余运算的优先级*相同*。这时为了得到正确的运算顺序,还需要考虑结合性。乘法运算与取余运算都是左结合的。可以将这考虑成,从它们的左边开始为这两部分表达式都隐式地加上括号: + +```swift +2 + ((3 % 4) * 5) +``` + +`(3 % 4)` 等于 `3`,所以表达式相当于: + +```swift +2 + (3 * 5) +``` + +`3 * 5` 等于 `15`,所以表达式相当于: + +```swift +2 + 15 +``` + +因此计算结果为 `17`。 + +有关 Swift 标准库提供的操作符信息,包括操作符优先级组和结核性设置的完整列表,请参见[操作符声明](https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations)。 + +> 注意 +> +> 相对 C 语言和 Objective-C 来说,Swift 的运算符优先级和结合性规则更加简洁和可预测。但是,这也意味着它们相较于 C 语言及其衍生语言并不是完全一致。在对现有的代码进行移植的时候,要注意确保运算符的行为仍然符合你的预期。 + +## 运算符函数 {#operator_functions} + +类和结构体可以为现有的运算符提供自定义的实现。这通常被称为运算符*重载*。 + +下面的例子展示了如何让自定义的结构体支持加法运算符(`+`)。算术加法运算符是一个*二元运算符*,因为它是对两个值进行运算,同时它还可以称为*中缀*运算符,因为它出现在两个值中间。 + +例子中定义了一个名为 `Vector2D` 的结构体用来表示二维坐标向量 `(x, y)`,紧接着定义了一个可以将两个 `Vector2D` 结构体实例进行相加的*运算符函数*: + +```swift +struct Vector2D { + var x = 0.0, y = 0.0 +} + +extension Vector2D { + static func + (left: Vector2D, right: Vector2D) -> Vector2D { + return Vector2D(x: left.x + right.x, y: left.y + right.y) + } +} +``` + +该运算符函数被定义为 `Vector2D` 上的一个类方法,并且函数的名字与它要进行重载的 `+` 名字一致。因为加法运算并不是一个向量必需的功能,所以这个类方法被定义在 `Vector2D` 的一个扩展中,而不是 `Vector2D` 结构体声明内。而算术加法运算符是二元运算符,所以这个运算符函数接收两个类型为 `Vector2D` 的参数,同时有一个 `Vector2D` 类型的返回值。 + +在这个实现中,输入参数分别被命名为 `left` 和 `right`,代表在 `+` 运算符左边和右边的两个 `Vector2D` 实例。函数返回了一个新的 `Vector2D` 实例,这个实例的 `x` 和 `y` 分别等于作为参数的两个实例的 `x` 和 `y` 的值之和。 + +这个类方法可以在任意两个 `Vector2D` 实例中间作为中缀运算符来使用: + +```swift +let vector = Vector2D(x: 3.0, y: 1.0) +let anotherVector = Vector2D(x: 2.0, y: 4.0) +let combinedVector = vector + anotherVector +// combinedVector 是一个新的 Vector2D 实例,值为 (5.0, 5.0) +``` + +这个例子实现两个向量 `(3.0,1.0)` 和 `(2.0,4.0)` 的相加,并得到新的向量 `(5.0,5.0)`。这个过程如下图示: + +![Art/vectorAddition_2x.png](https://docs.swift.org/swift-book/_images/vectorAddition_2x.png) + +### 前缀和后缀运算符 {#prefix_and_postfix_operators} + +上个例子演示了一个二元中缀运算符的自定义实现。类与结构体也能提供标准*一元运算符*的实现。一元运算符只运算一个值。当运算符出现在值之前时,它就是*前缀*的(例如 `-a`),而当它出现在值之后时,它就是*后缀*的(例如 `b!`)。 + +要实现前缀或者后缀运算符,需要在声明运算符函数的时候在 `func` 关键字之前指定 `prefix` 或者 `postfix` 修饰符: + +```swift +extension Vector2D { + static prefix func - (vector: Vector2D) -> Vector2D { + return Vector2D(x: -vector.x, y: -vector.y) + } +} +``` + +这段代码为 `Vector2D` 类型实现了一元运算符(`-a`)。由于该运算符是前缀运算符,所以这个函数需要加上 `prefix` 修饰符。 + +对于简单数值,一元负号运算符可以对它们的正负性进行改变。对于 `Vector2D` 来说,该运算将其 `x` 和 `y` 属性的正负性都进行了改变: + +```swift +let positive = Vector2D(x: 3.0, y: 4.0) +let negative = -positive +// negative 是一个值为 (-3.0, -4.0) 的 Vector2D 实例 +let alsoPositive = -negative +// alsoPositive 是一个值为 (3.0, 4.0) 的 Vector2D 实例 +``` + +### 复合赋值运算符 {#compound_assignment_operators} + +*复合赋值运算符*将赋值运算符(`=`)与其它运算符进行结合。例如,将加法与赋值结合成加法赋值运算符(`+=`)。在实现的时候,需要把运算符的左参数设置成 `inout` 类型,因为这个参数的值会在运算符函数内直接被修改。 + +在下面的例子中,对 `Vector2D` 实例实现了一个加法赋值运算符函数: + +在下面的例子中,对 `Vector2D` 实例实现了一个加法赋值运算符函数: + +```swift +extension Vector2D { + static func += (left: inout Vector2D, right: Vector2D) { + left = left + right + } +} +``` + +因为加法运算在之前已经定义过了,所以在这里无需重新定义。在这里可以直接利用现有的加法运算符函数,用它来对左值和右值进行相加,并再次赋值给左值: + +```swift +var original = Vector2D(x: 1.0, y: 2.0) +let vectorToAdd = Vector2D(x: 3.0, y: 4.0) +original += vectorToAdd +// original 的值现在为 (4.0, 6.0) +``` + +> 注意 +> +> 不能对默认的赋值运算符(`=`)进行重载。只有复合赋值运算符可以被重载。同样地,也无法对三元条件运算符 (`a ? b : c`) 进行重载。 + +### 等价运算符 {#equivalence_operators} + +通常情况下,自定义的类和结构体没有对*等价运算符*进行默认实现,等价运算符通常被称为*相等*运算符(`==`)与*不等*运算符(`!=`)。 + +为了使用等价运算符对自定义的类型进行判等运算,需要为“相等”运算符提供自定义实现,实现的方法与其它中缀运算符一样, 并且增加对标准库 `Equatable` 协议的遵循: + +```swift +extension Vector2D: Equatable { + static func == (left: Vector2D, right: Vector2D) -> Bool { + return (left.x == right.x) && (left.y == right.y) + } +} +``` + +上述代码实现了“相等”运算符(`==`)来判断两个 `Vector2D` 实例是否相等。对于 `Vector2D` 来说,“相等”意味着“两个实例的 `x` 和 `y` 都相等”,这也是代码中用来进行判等的逻辑。如果你已经实现了“相等”运算符,通常情况下你并不需要自己再去实现“不等”运算符(`!=`)。标准库对于“不等”运算符提供了默认的实现,它简单地将“相等”运算符的结果进行取反后返回。 + +现在我们可以使用这两个运算符来判断两个 `Vector2D` 实例是否相等: + +```swift +let twoThree = Vector2D(x: 2.0, y: 3.0) +let anotherTwoThree = Vector2D(x: 2.0, y: 3.0) +if twoThree == anotherTwoThree { + print("These two vectors are equivalent.") +} +// 打印“These two vectors are equivalent.” +``` + +Swift 为以下数种自定义类型提供等价运算符的默认实现: + +- 只拥有存储属性,并且它们全都遵循 `Equatable` 协议的结构体 +- 只拥有关联类型,并且它们全都遵循 `Equatable` 协议的枚举 +- 没有关联类型的枚举 + +在类型原始的声明中声明遵循 `Equatable` 来接收这些默认实现。 + +下面为三维位置向量 `(x, y, z)` 定义的 `Vector3D` 结构体,与 `Vector2D` 类似。由于 `x`,`y` 和 `z` 属性都是 `Equatable` 类型,`Vector3D` 获得了默认的等价运算符实现。 + +```swift +struct Vector3D: Equatable { + var x = 0.0, y = 0.0, z = 0.0 +} + +let twoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0) +let anotherTwoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0) +if twoThreeFour == anotherTwoThreeFour { + print("These two vectors are also equivalent.") +} +// 打印“These two vectors are also equivalent.” +``` + +## 自定义运算符 {#custom_operators} + +除了实现标准运算符,在 Swift 中还可以声明和实现*自定义运算符*。可以用来自定义运算符的字符列表请参考[运算符](../chapter3/02_Lexical_Structure.html#operators)。 + +新的运算符要使用 `operator` 关键字在全局作用域内进行定义,同时还要指定 `prefix`、`infix` 或者 `postfix` 修饰符: + +```swift +prefix operator +++ +``` + +上面的代码定义了一个新的名为 `+++` 的前缀运算符。对于这个运算符,在 Swift 中并没有已知的意义,因此在针对 `Vector2D` 实例的特定上下文中,给予了它自定义的意义。对这个示例来讲,`+++` 被实现为“前缀双自增”运算符。它使用了前面定义的复合加法运算符来让矩阵与自身进行相加,从而让 `Vector2D` 实例的 `x` 属性和 `y` 属性值翻倍。你可以像下面这样通过对 `Vector2D` 添加一个 `+++` 类方法,来实现 `+++` 运算符: + +```swift +extension Vector2D { + static prefix func +++ (vector: inout Vector2D) -> Vector2D { + vector += vector + return vector + } +} + +var toBeDoubled = Vector2D(x: 1.0, y: 4.0) +let afterDoubling = +++toBeDoubled +// toBeDoubled 现在的值为 (2.0, 8.0) +// afterDoubling 现在的值也为 (2.0, 8.0) +``` + +### 自定义中缀运算符的优先级 {#precedence_and_associativity_for_custom_infix_operators} + +每个自定义中缀运算符都属于某个优先级组。优先级组指定了这个运算符相对于其他中缀运算符的优先级和结合性。[优先级和结合性](#precedence_and_associativity)中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。 + +而没有明确放入某个优先级组的自定义中缀运算符将会被放到一个默认的优先级组内,其优先级高于三元运算符。 + +以下例子定义了一个新的自定义中缀运算符 `+-`,此运算符属于 `AdditionPrecedence` 优先组: + +```swift +infix operator +-: AdditionPrecedence +extension Vector2D { + static func +- (left: Vector2D, right: Vector2D) -> Vector2D { + return Vector2D(x: left.x + right.x, y: left.y - right.y) + } +} +let firstVector = Vector2D(x: 1.0, y: 2.0) +let secondVector = Vector2D(x: 3.0, y: 4.0) +let plusMinusVector = firstVector +- secondVector +// plusMinusVector 是一个 Vector2D 实例,并且它的值为 (4.0, -2.0) +``` + +这个运算符把两个向量的 `x` 值相加,同时从第一个向量的 `y` 中减去第二个向量的 `y` 。因为它本质上是属于“相加型”运算符,所以将它放置在 `+` 和 `-` 等默认中缀“相加型”运算符相同的优先级组中。关于 Swift 标准库提供的运算符,以及完整的运算符优先级组和结合性设置,请参考 [运算符声明](https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations)。而更多关于优先级组以及自定义操作符和优先级组的语法,请参考[运算符声明](https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID380)。 + +> 注意 +> +> 当定义前缀与后缀运算符的时候,我们并没有指定优先级。然而,如果对同一个值同时使用前缀与后缀运算符,则后缀运算符会先参与运算。 \ No newline at end of file From c02ecdcc09c246210ed3194a3c4f9d03293aa3c4 Mon Sep 17 00:00:00 2001 From: edz Date: Wed, 27 Mar 2019 20:12:00 +0800 Subject: [PATCH 8/8] =?UTF-8?q?=E9=81=BF=E5=85=8D=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/26_Advanced_Operators.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/source/chapter2/26_Advanced_Operators.md b/source/chapter2/26_Advanced_Operators.md index 0464257f..21dd7ad2 100644 --- a/source/chapter2/26_Advanced_Operators.md +++ b/source/chapter2/26_Advanced_Operators.md @@ -264,7 +264,7 @@ signedOverflow = signedOverflow &- 1 2 + 15 ``` -因此计算结果为 `17`。 +因此计算结果为 `17`。 有关 Swift 标准库提供的操作符信息,包括操作符优先级组和结核性设置的完整列表,请参见[操作符声明](https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations)。 @@ -337,9 +337,7 @@ let alsoPositive = -negative ### 复合赋值运算符 {#compound_assignment_operators} -*复合赋值运算符*将赋值运算符(`=`)与其它运算符进行结合。例如,将加法与赋值结合成加法赋值运算符(`+=`)。在实现的时候,需要把运算符的左参数设置成 `inout` 类型,因为这个参数的值会在运算符函数内直接被修改。 - -在下面的例子中,对 `Vector2D` 实例实现了一个加法赋值运算符函数: +*复合赋值运算符*将赋值运算符(`=`)与其它运算符进行结合。例如,将加法与赋值结合成加法赋值运算符(`+=`)。在实现的时候,需要把运算符的左参数设置成 `inout` 类型,因为这个参数的值会在运算符函数内直接被修改。 在下面的例子中,对 `Vector2D` 实例实现了一个加法赋值运算符函数: @@ -465,4 +463,4 @@ let plusMinusVector = firstVector +- secondVector > 注意 > -> 当定义前缀与后缀运算符的时候,我们并没有指定优先级。然而,如果对同一个值同时使用前缀与后缀运算符,则后缀运算符会先参与运算。 \ No newline at end of file +> 当定义前缀与后缀运算符的时候,我们并没有指定优先级。然而,如果对同一个值同时使用前缀与后缀运算符,则后缀运算符会先参与运算。