chapter2 23~26 细节优化 (#860)

* 23_Automatic_Reference_Counting 修正编辑格式,更新代码

* 24_Memory_Safety 删除多余空格与空行

* 25_Access_Control 修正标点使用,更新代码

* 26_Advanced_Operators 修正编辑小错误
This commit is contained in:
BqLin
2019-01-22 23:04:23 +08:00
committed by Jie Liang
parent 4c3ad3d2f8
commit 8c267a3de8
4 changed files with 19 additions and 19 deletions

View File

@ -18,7 +18,7 @@ Swift 使用*自动引用计数ARC*机制来跟踪和管理你的应用程
然而,当 ARC 收回和释放了正在被使用中的实例,该实例的属性和方法将不能再被访问和调用。实际上,如果你试图访问这个实例,你的应用程序很可能会崩溃。
为了确保使用中的实例不会被销毁ARC 会跟踪和计算每一个实例正在被多少属性常量和变量所引用。哪怕实例的引用数为1ARC 都不会销毁这个实例。
为了确保使用中的实例不会被销毁ARC 会跟踪和计算每一个实例正在被多少属性,常量和变量所引用。哪怕实例的引用数为 1ARC 都不会销毁这个实例。
为了使上述成为可能,无论你将实例赋值给属性、常量或变量,它们都会创建此实例的强引用。之所以称之为“强”引用,是因为它会将实例牢牢地保持住,只要强引用还在,实例是不允许被销毁的。
@ -325,6 +325,7 @@ john = nil
最后的代码展示了在 `john` 变量被设为 `nil``Customer` 实例和 `CreditCard` 实例的析构器都打印出了“销毁”的信息。
> 注意
>
> 上面的例子展示了如何使用安全的无主引用。对于需要禁用运行时的安全检查的情况例如出于性能方面的原因Swift 还提供了不安全的无主引用。与所有不安全的操作一样,你需要负责检查代码以确保其安全性。
> 你可以通过 `unowned(unsafe)` 来声明不安全无主引用。如果你试图在实例被销毁后,访问该实例的不安全无主引用,你的程序会尝试访问该实例之前所在的内存地址,这是一个不安全的操作。
@ -502,7 +503,7 @@ lazy var someClosure: (Int, String) -> String = {
如果闭包没有指明参数列表或者返回类型,它们会通过上下文推断,那么可以把捕获列表和关键字 `in` 放在闭包最开始的地方:
```swift
lazy var someClosure: Void -> String = {
lazy var someClosure: () -> String = {
[unowned self, weak delegate = self.delegate!] in
// 这里是闭包的函数体
}
@ -527,7 +528,7 @@ class HTMLElement {
let name: String
let text: String?
lazy var asHTML: Void -> String = {
lazy var asHTML: () -> String = {
[unowned self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"

View File

@ -34,7 +34,6 @@ print("We're number \(one)!")
> 如果你曾经在单线程代码里有访问冲突Swift 可以保证你在编译或者运行时会得到错误。对于多线程的代码,可以使用 [Thread Sanitizer](https://developer.apple.com/documentation/code_diagnostics/thread_sanitizer) 去帮助检测多线程的冲突。
<a name="characteristics_of_memory_access"></a>
### 内存访问性质
内存访问冲突时,要考虑内存访问上下文中的这三个性质:访问是读还是写,访问的时长,以及被访问的存储地址。特别是,冲突会发生在当你有两个访问符合下列的情况:
@ -67,7 +66,7 @@ print(myNumber)
一个函数会对它所有的 in-out 参数进行长期写访问。in-out 参数的写访问会在所有非 in-out 参数处理完之后开始,直到函数执行完毕为止。如果有多个 in-out 参数,则写访问开始的顺序与参数的顺序一致。
长期访问的存在会造成一个结果,你不能在访问以 in-out 形式传入后的原变量,即使作用域原则和访问权限允许 —— 任何访问原变量的行为都会造成冲突。例如:
长期访问的存在会造成一个结果,你不能在访问以 in-out 形式传入后的原变量,即使作用域原则和访问权限允许——任何访问原变量的行为都会造成冲突。例如:
```swift
var stepSize = 1
@ -162,12 +161,11 @@ oscar.shareHealth(with: &oscar)
// 错误oscar 访问冲突
```
mutating 方法在调用期间需要对 `self` 发起写访问,而同时 in-out 参数也需要写访问。在方法里,`self``teammate` 都指向了同一个存储地址 —— 就像下面展示的那样。对于同一块内存同时进行两个写访问,并且它们重叠了,就此产生了冲突。
mutating 方法在调用期间需要对 `self` 发起写访问,而同时 in-out 参数也需要写访问。在方法里,`self``teammate` 都指向了同一个存储地址——就像下面展示的那样。对于同一块内存同时进行两个写访问,并且它们重叠了,就此产生了冲突。
![](https://docs.swift.org/swift-book/_images/memory_share_health_oscar_2x.png)
<a name="conflicting_access_to_properties"></a>
## 属性的访问冲突
如结构体,元组和枚举的类型都是由多个独立的值组成的,例如结构体的属性或元组的元素。因为它们都是值类型,修改值的任何一部分都是对于整个值的修改,意味着其中一个属性的读或写访问都需要访问整一个值。例如,元组元素的写访问重叠会产生冲突:
@ -198,12 +196,10 @@ func someFunction() {
上面的例子里,`oscar``health``energy` 都作为 in-out 参数传入了 `balance(_:_:)` 里。编译器可以保证内存安全,因为两个存储属性任何情况下都不会相互影响。
限制结构体属性的重叠访问对于保证内存安全不是必要的。保证内存安全是必要的,但因为访问独占权的要求比内存安全还要更严格 —— 意味着即使有些代码违反了访问独占权的原则,也是内存安全的,所以如果编译器可以保证这种非专属的访问是安全的,那 Swift 就会允许这种行为的代码运行。特别是当你遵循下面的原则时,它可以保证结构体属性的重叠访问是安全的:
限制结构体属性的重叠访问对于保证内存安全不是必要的。保证内存安全是必要的,但因为访问独占权的要求比内存安全还要更严格——意味着即使有些代码违反了访问独占权的原则,也是内存安全的,所以如果编译器可以保证这种非专属的访问是安全的,那 Swift 就会允许这种行为的代码运行。特别是当你遵循下面的原则时,它可以保证结构体属性的重叠访问是安全的:
* 你访问的是实例的存储属性,而不是计算属性或类的属性
* 结构体是本地变量的值,而非全局变量
* 结构体要么没有被闭包捕获,要么只被非逃逸闭包捕获了
如果编译器无法保证访问的安全性,它就不会允许那次访问。

View File

@ -81,7 +81,7 @@ Swift 中的访问级别遵循一个基本原则:*不可以在某个实体中
<a name="access_control_syntax"></a>
## 访问控制语法
通过修饰符 `open``public``internal``fileprivate``private` 来声明实体的访问级别:
通过修饰符 `open``public``internal``fileprivate``private` 来声明实体的访问级别:
```swift
public class SomePublicClass {}
@ -135,8 +135,7 @@ fileprivate class SomeFilePrivateClass { // 显式 fileprivate 类
private class SomePrivateClass { // 显式 private 类
func somePrivateMethod() {} // 隐式 private 类成员
}
```swift
```
<a name="tuple_types"></a>
### 元组类型
@ -208,7 +207,11 @@ public enum CompassPoint {
```swift
public class A {
<<<<<<< HEAD
private func someMethod() {}
=======
fileprivate func someMethod() {}
>>>>>>> /25_Access_Control 修正标点使用,更新代码
}
internal class B: A {
@ -220,7 +223,7 @@ internal class B: A {
```swift
public class A {
private func someMethod() {}
fileprivate func someMethod() {}
}
internal class B: A {
@ -370,7 +373,7 @@ Extension 可以在访问级别允许的情况下对类、结构体、枚举进
```swift
protocol SomeProtocol {
func doSomething() {}
func doSomething()
}
```

View File

@ -240,7 +240,7 @@ signedOverflow = signedOverflow &- 1
运算符的*优先级*使得一些运算符优先于其他运算符;它们会先被执行。
*结合性*定义了相同优先级的运算符是如何结合的,也就是说,是与左边结合为一组,还是与右边结合为一组。可以将其理解为“它们是与左边的表达式结合的”或者“它们是与右边的表达式结合的
*结合性*定义了相同优先级的运算符是如何结合的,也就是说,是与左边结合为一组,还是与右边结合为一组。可以将其理解为“它们是与左边的表达式结合的”或者“它们是与右边的表达式结合的”
当考虑一个复合表达式的计算顺序时,运算符的优先级和结合性是非常重要的。举例来说,运算符优先级解释了为什么下面这个表达式的运算结果会是 `17`
@ -424,7 +424,7 @@ let anotherTwoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
if twoThreeFour == anotherTwoThreeFour {
print("These two vectors are also equivalent.")
}
// Prints "These two vectors are also equivalent."
// 打印“These two vectors are also equivalent.
```
<a name="custom_operators"></a>
@ -476,8 +476,8 @@ 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)
这个运算符把两个向量的 `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)
> 注意
>
> 当定义前缀与后缀运算符的时候,我们并没有指定优先级。然而,如果对同一个值同时使用前缀与后缀运算符,则后缀运算符会先参与运算。
> 当定义前缀与后缀运算符的时候,我们并没有指定优先级。然而,如果对同一个值同时使用前缀与后缀运算符,则后缀运算符会先参与运算。