2
2
3
3
通过合理地组织代码来避免内存访问冲突。
4
4
5
- 默认情况下,Swift 会阻止代码中不安全的行为。例如,Swift 会确保所有变量都在使用前被初始化、内存不可在被释放后访问, 还会对数组的下标做越界检查。
5
+ 默认情况下,Swift 会阻止代码中不安全的行为。例如,Swift 会确保所有变量都在使用前被初始化、内存不可在被释放后访问、 还会对数组的下标做越界检查。
6
6
7
- Swift 还会要求修改内存的代码拥有对被修改区域的独占访问权 ,以确保对同一块内存区域的多次访问不会产生冲突。由于 Swift 会自动管理内存,大多数情况下你不需要考虑内存访问相关的问题。但是,为了避免编写出会产生内存访问冲突的代码,你还是很有必要了解什么情况下会出现这种问题。如果你的代码有内存访问冲突的问题,系统会在编译时或运行时报错。
7
+ Swift 会要求修改内存的代码拥有对被修改区域的独占访问权 ,以确保对同一块内存区域的多次访问不会产生冲突。由于 Swift 会自动管理内存,大多数情况下你不需要考虑内存访问相关的问题。但是,为了避免编写出会产生内存访问冲突的代码,你还是很有必要了解什么情况下会出现这种问题。如果你的代码有内存访问冲突的问题,系统会在编译时或运行时报错。
8
8
9
9
<!--
10
10
TODO: maybe re-introduce this text...
@@ -46,7 +46,7 @@ print("We're number \(one)!")
46
46
47
47
当代码中的不同部分试图访问同一块内存区域时,访问冲突就有可能出现。对一块内存区域的同时访问可能导致程序出现无法预测或不稳定的行为。Swift 中有许多修改数值的方式,其中一些会横跨多行代码,这意味着修改某个数值的过程本身也有可能产生对此数值的访问。
48
48
49
- 要理解这个问题,你可以尝试想象一下在纸上更新一个预算表的流程。更新预算表分为两步, 第一步你需要先添加每个预算项目的名字和数额,第二步才是更新预算总额。在整个更新流程的之前及之后,你可以从预算中读取任何信息,而这些信息都是正确的,就像下图所示一样。
49
+ 要理解这个问题,你可以尝试想象一下在纸上更新一个预算表的流程。更新预算表分为两步: 第一步你需要先添加每个预算项目的名字和数额,第二步才是更新预算总额。在整个更新流程的之前及之后,你可以从预算中读取任何信息,而这些信息都是正确的,就像下图所示一样。
50
50
51
51
![ ] ( memory_shopping )
52
52
@@ -71,7 +71,7 @@ print("We're number \(one)!")
71
71
- 它们访问的是同一块内存区域。
72
72
- 它们的时间窗口出现了重叠。
73
73
74
- 读操作和写操作之间的区别是显而易见的:写操作会改变内存区域,而读操作不会。「内存区域」指的是被访问的内容(例如变量、常量、属性)。内存访问的时长要么瞬间完成 ,要么持续较长时间。
74
+ 读操作和写操作之间的区别是显而易见的:写操作会改变内存区域,而读操作不会。「内存区域」指的是被访问的内容(例如变量、常量、属性)。内存访问的要么瞬间完成 ,要么持续较长时间。
75
75
76
76
如果一项操作仅仅使用了 C 的原子化操作,则这项操作本身也是** 原子化** 的。请查看 ` stdatomic(3) ` 手册来了解有哪些函数满足这个定义。
77
77
@@ -236,7 +236,7 @@ balance(&playerOneScore, &playerOneScore)
236
236
237
237
238
238
239
- ## 在方法中对 self 的访问冲突
239
+ ## 方法中的 self 访问冲突
240
240
241
241
<!--
242
242
This (probably?) applies to all value types,
@@ -267,6 +267,7 @@ struct Player {
267
267
```
268
268
269
269
<!--
270
+
270
271
- test: `memory-player-share-with-self`
271
272
272
273
```swifttest
@@ -318,31 +319,15 @@ oscar.shareHealth(with: &maria) // OK
318
319
```
319
320
-->
320
321
321
- 在上面的例子中,通过调用 ` shareHealth(with:) ` 方法来将
322
-
323
- In the example above,
324
- calling the ` shareHealth(with:) ` method
325
- for Oscar's player to share health with Maria's player
326
- doesn't cause a conflict.
327
- There's a write access to ` oscar ` during the method call
328
- because ` oscar ` is the value of ` self ` in a mutating method,
329
- and there's a write access to ` maria `
330
- for the same duration
331
- because ` maria ` was passed as an in-out parameter.
332
- As shown in the figure below,
333
- they access different locations in memory.
334
- Even though the two write accesses overlap in time,
335
- they don't conflict.
322
+ 在上面的例子中,通过调用 ` shareHealth(with:) ` 方法来将 Oscar 的生命值分享给 Maria 并不会造成冲突。因为 ` oscar ` 是变值方法中 ` self ` 所对应的值,所以方法执行过程存在对于 ` oscar ` 的写访问;在相同的时间窗口内,方法对于 ` maria ` 也会有写访问,因为 ` maria ` 是作为 in-out 参数传入的。尽管这两次写访问在时间上发生了重叠,它们并不冲突。
336
323
337
324
![ ] ( memory_share_health_maria )
338
325
339
- However,
340
- if you pass ` oscar ` as the argument to ` shareHealth(with:) ` ,
341
- there's a conflict:
326
+ 然而,如果你将 ` oscar ` 所谓参数传入 ` shareHealth(with:) ` ,就会产生冲突了:
342
327
343
328
``` swift
344
329
oscar.shareHealth (with : & oscar)
345
- // Error: conflicting accesses to oscar
330
+ // 错误:对 oscar 的访问出现冲突
346
331
```
347
332
348
333
<!--
@@ -366,37 +351,18 @@ oscar.shareHealth(with: &oscar)
366
351
```
367
352
-->
368
353
369
- The mutating method needs write access to ` self `
370
- for the duration of the method,
371
- and the in-out parameter needs write access to ` teammate `
372
- for the same duration.
373
- Within the method,
374
- both ` self ` and ` teammate ` refer to
375
- the same location in memory ---
376
- as shown in the figure below.
377
- The two write accesses
378
- refer to the same memory and they overlap,
379
- producing a conflict.
354
+ 在整个变值方法执行期间,方法不仅需要对保持对 ` self ` 的写访问,其 in-out 参数还需要对 ` teammate ` 保持相同时长的写访问。在方法内,` self ` 和 ` teammate ` 都指向了内存中的同一位置(如下图所示)。因为两次写访问不仅在时间上重叠,访问的内存区域也重叠了,所以产生了冲突。
380
355
381
356
![ ] ( memory_share_health_oscar )
382
357
383
- ## Conflicting Access to Properties
358
+ ## 属性访问冲突
384
359
385
- Types like structures, tuples, and enumerations
386
- are made up of individual constituent values,
387
- such as the properties of a structure or the elements of a tuple.
388
- Because these are value types, mutating any piece of the value
389
- mutates the whole value,
390
- meaning read or write access to one of the properties
391
- requires read or write access to the whole value.
392
- For example,
393
- overlapping write accesses to the elements of a tuple
394
- produces a conflict:
360
+ 结构体、元组、枚举这样的类型是由多个独立的值构成的(例如结构题的属性、元组的元素)。它们都是值类型,所以修改值的任何一部分都是对于整个值的修改。这意味着对于其中任何一个属性的读或写访问,都需要对于整个值的访问。例如,对于元组元素的重叠写访问会造成冲突:
395
361
396
362
``` swift
397
363
var playerInformation = (health : 10 , energy : 20 )
398
364
balance (& playerInformation.health , & playerInformation.energy )
399
- // Error: conflicting access to properties of playerInformation
365
+ // 错误:对 playerInformation 的属性访问有冲突
400
366
```
401
367
402
368
<!--
@@ -417,28 +383,13 @@ balance(&playerInformation.health, &playerInformation.energy)
417
383
```
418
384
-->
419
385
420
- In the example above,
421
- calling ` balance(_:_:) ` on the elements of a tuple
422
- produces a conflict
423
- because there are overlapping write accesses to ` playerInformation ` .
424
- Both ` playerInformation.health ` and ` playerInformation.energy `
425
- are passed as in-out parameters,
426
- which means ` balance(_:_:) ` needs write access to them
427
- for the duration of the function call.
428
- In both cases, a write access to the tuple element
429
- requires a write access to the entire tuple.
430
- This means there are two write accesses to ` playerInformation `
431
- with durations that overlap,
432
- causing a conflict.
433
-
434
- The code below shows that the same error appears
435
- for overlapping write accesses
436
- to the properties of a structure
437
- that's stored in a global variable.
386
+ 在上方的示例中,因为出现了对于 ` playerInformation ` 的重叠写访问,对元组中的元素调用 ` balance(_:_:) ` 就会产生冲突。` playerInformation.health ` 和 ` playerInformation.energy ` 都被作为 in-out 参数被传入了,这意味着 ` balance(_:_:) ` 在执行期间需要保持对它们的写访问,而这又意味着 ` balance(_:_:) ` 需要保持两次对 ` playerInformation ` 整体的重叠写访问,这样一来就发生了访问冲突。
387
+
388
+ 下方的代码展示了对一个存储在全局变量中的结构体的重叠写访问,这会导致相同的错误。
438
389
439
390
``` swift
440
391
var holly = Player (name : " Holly" , health : 10 , energy : 10 )
441
- balance (& holly.health , & holly.energy ) // Error
392
+ balance (& holly.health , & holly.energy ) // 错误
442
393
```
443
394
444
395
<!--
@@ -463,14 +414,7 @@ balance(&holly.health, &holly.energy) // Error
463
414
```
464
415
-->
465
416
466
- In practice,
467
- most access to the properties of a structure
468
- can overlap safely.
469
- For example,
470
- if the variable ` holly ` in the example above
471
- is changed to a local variable instead of a global variable,
472
- the compiler can prove that overlapping access
473
- to stored properties of the structure is safe:
417
+ 在实践中,大多数时候对于结构体属性的重叠访问是安全的。举个例子,如果上方例子中的变量 ` holly ` 是一个本地变量而非全局变量,编译器可以「证明」对于该结构体的属性的重叠访问是安全的:
474
418
475
419
``` swift
476
420
func someFunction () {
@@ -480,6 +424,7 @@ func someFunction() {
480
424
```
481
425
482
426
<!--
427
+
483
428
- test: `memory-share-health-local`
484
429
485
430
```swifttest
@@ -501,34 +446,15 @@ func someFunction() {
501
446
```
502
447
-->
503
448
504
- In the example above,
505
- Oscar's health and energy are passed
506
- as the two in-out parameters to ` balance(_:_:) ` .
507
- The compiler can prove that memory safety is preserved
508
- because the two stored properties don't interact in any way.
509
-
510
- The restriction against
511
- overlapping access to properties of a structure
512
- isn't always necessary to preserve memory safety.
513
- Memory safety is the desired guarantee,
514
- but exclusive access is a stricter requirement than memory safety ---
515
- which means some code preserves memory safety,
516
- even though it violates exclusive access to memory.
517
- Swift allows this memory-safe code if the compiler can prove
518
- that the nonexclusive access to memory is still safe.
519
- Specifically, it can prove
520
- that overlapping access to properties of a structure is safe
521
- if the following conditions apply:
522
-
523
- - You're accessing only stored properties of an instance,
524
- not computed properties or class properties.
525
- - The structure is the value of a local variable,
526
- not a global variable.
527
- - The structure is either not captured by any closures,
528
- or it's captured only by nonescaping closures.
529
-
530
- If the compiler can't prove the access is safe,
531
- it doesn't allow the access.
449
+ 在上方的例子中,Oscar 的生命值和能量值被作为两个 in-out 参数传递给了 ` balance(_:_:) ` 。此时编译器能够证明内存是安全的,因为这两个属性并不会以任何方式交互。
450
+
451
+ 要保证内存安全,限制结构体属性的重叠访问并不总是必要的。内存安全性是我们想获得的一种保证,但是「独占访问」是相比「内存安全」更加严格的一种要求 —— 这意味着即使有些代码违反了「独占访问」的原则,它也可以是内存安全的。只要编译器可以「证明」这种非专属的访问是内存安全的,Swift 就会允许这样的代码存在。特别地,在以下条件满足时,编译器就可以证明对结构体属性的重叠访问是安全的:
452
+
453
+ - 代码只访问了实例的存储属性,而没有访问计算属性或类属性
454
+ - 结构体是本地变量,而非全局变量的值
455
+ - 结构体要么没有被闭包捕获,要么只被非逃逸闭包捕获了
456
+
457
+ 如果编译器无法证明访问安全性,它就会拒绝访问。
532
458
533
459
<!--
534
460
Because there's no syntax
0 commit comments