Memory Safety

  • 内存访问分为读写,在以下三种条件同时满足时会造成内存访问冲突。
    1.至少一个是写
    2.访问同一块地址
    3.访问期间重叠
  • 内存访问又分为立即访问(大多数情况),和长期访问。造成访问冲突主要由函数的in-out参数和结构体的mutating方法造成。
  • 原值作为inout进行写入时,你不能够访问该原值。实例如下:
    var stepSize = 1
    func incrementInPlace(_ number: inout Int) {
      number += stepSize
    }
    incrementInPlace(&stepSize)
    
    number和stepSize指向同一块地址,读写访问同时发生,会访问冲突
    解决方案如下:
    // Make an explicit copy.
    var copyOfStepSize = stepSize
    incrementInPlace(©OfStepSize)
    
     // Update the original.
    stepSize = copyOfStepSize
    
  • 函数需要传入多个inout类型参数时,不能传入同一变量。实例如下:
    func balance(_ x: inout Int, _ y: inout Int) {
      let sum = x + y
      x = sum / 2
      y = sum - x
    }
    var playerOneSorce = 42
    var playerTwoSorce = 30
    balance(&playerOneSorce, &playerTwoSorce)
    //balance(&playerOneSorce, &playerOneSorce) error
    
    报错inout参数不能传入别名一样。跟函数体无关,即使函数体内为空也会报错。
  • inout作为参数表示整个函数都是该变量的访问期间。所以前一例在长期写访问同时进行了读访问。后一例对同一变量同时进行两次长期写访问。
  • 同时注意省略Inout的方法,通过在调用时使用&取地址分辨。
  • 注意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 teamate: inout Player) {
          balance(&teamate.health, &health)
      }
    }
    
    var oscar = Player.init(name: "Oscar", health: 10, energy: 10)
    var maria = Player.init(name: "Maria", health: 5, energy: 5)
    oscar.shareHealth(with: &maria)
    print(oscar.health)
    //oscar.shareHealth(with: &oscar)
    
    前者为同时对oscar和maria进行写操作,没问题。后者同时对oscar进行写操作,会造成错误。
  • 对于结构体枚举的属性以及元组的元素来说,对其进行写操作等同于对整个类型进行写操作,一样会造成冲突,由于其是值类型造成的。如下:
    var playerInformation = (health: 10, energy: 20)
    balance(&playerInformation.health, &playerInformation.energy)
    
    对元组同时进行写操作
    var holly = Player(name: "Holly", health: 10, energy: 10)
    balance(&holly.health, &holly.energy)
    
    对结构体同时进行写操作
  • 注意以上的错误针对于全局变量,对于局部变量则没事,如下:
    func someFunction() {
      var oscar = Player(name: "Oscar", health: 10, energy: 10)
      balance(&oscar.health, &oscar.energy)  // OK
    }
    
  • swift提供了一些可以保证内存安全的措施,当满足以下时
    1.访问实例存储属性,而非类属性或计算属性
    2.结构体是局部变量,非全局变量
    3.结构体不被闭包捕捉,或被非逃逸闭包捕捉

你可能感兴趣的:(Memory Safety)