Swift 的内存安全

2017-11-12  本文已影响0人  KeenV
内存访问的特点

如果有两个访问动作出现在以下情况中,便会发生冲突

  1. 至少有一个动作是写入访问
  2. 访问的是同一块内存
  3. 访问的时间有重叠

访问内存分为即时的和长期的

  1. 即时的访问:如果其它代码在访问开始之后至结束之前无法运行,则访问是即时的。 所以,两个即时访问不能同时发生。 大部分内存访问是即时的。
以下所有的访问都是即时的
func oneMore(than number: Int) -> Int {
    return number + 1
}
 
var myNumber = 1
myNumber = oneMore(than: myNumber)
print(myNumber)
// Prints "2"
  1. 长期的访问:其它代码可能在访问开始之后至结束之前运行,称为重叠(overlap),一个长期访问可以和另一个长期访问或即时访问重叠。

重叠访问:主要出现在函数的in-out参数和结构中的方法和可变方法中。

in-out参数的访问冲突

函数对它的in-out参数有写入的长期访问

不能对传递给in-out参数的变量有任何的访问,否则会产生冲突

var stepSize = 1
 
func incrementInPlace(_ number: inout Int) {
    number += stepSize
}
 
incrementInPlace(&stepSize)
// Error: conflicting accesses to stepSize

不能把同一个变量传递给同一函数的不同in-out参数,否则会产生冲突

func balance(_ x: inout Int, _ y: inout Int) {
    let sum = x + y
    x = sum / 2
    y = sum - x
}
var playerOneScore = 42
var playerTwoScore = 30
balance(&playerOneScore, &playerTwoScore)  // OK
balance(&playerOneScore, &playerOneScore)
方法内self的访问冲突

一个可变方法调用时对self有长期访问

struct Player {
    var name: String
    var health: Int
    var energy: Int
    
    static let maxHealth = 10
    mutating func restoreHealth() {
        health = Player.maxHealth
    }
}

extension Player {
    mutating func shareHealth(with teammate: inout Player) {
        balance(&teammate.health, &health)
    }
}
 
var oscar = Player(name: "Oscar", health: 10, energy: 10)
var maria = Player(name: "Maria", health: 5, energy: 10)
oscar.shareHealth(with: &maria)  // OK
未产生冲突
当传递oscar给参数时,会产生冲突
oscar.shareHealth(with: &oscar)
image.png
属性的访问冲突

结构体、元组和枚举的都是值类型,对类型中的值改变会改变整个值,所以对一个属性的读取或写入访问需要读取或写入访问整个值。

把变量传递给in-out参数,会在整个函数调用期间对变量有写入的访问,
下列例子对元组有两个定入的访问,所以会产生冲突。
var playerInformation = (health: 10, energy: 20)
balance(&playerInformation.health, &playerInformation.energy)
// Error: conflicting access to properties of playerInformation
全局的存储性结构体变量也会产生同样的错误
var holly = Player(name: "Holly", health: 10, energy: 10)
balance(&holly.health, &holly.energy)  // Error
编译器会保证局部的结构体变量的重叠是安全的
func someFunction() {
    var oscar = Player(name: "Oscar", health: 10, energy: 10)
    balance(&oscar.health, &oscar.energy)  // OK
}

满足以下条件的重叠是安全的

  1. 仅仅访问的实例的存储性属性,没有计算性属性和类属性
  2. 结构体是局部变量,不是全局变量
  3. 结构体要么不被闭包捕获,要么只能被nonescaping闭包捕获。
    原文地址
上一篇 下一篇

猜你喜欢

热点阅读