内存安全

  1. 在inout参数的函数
    1. inout参数的变量会在函数内进行长期的写访问,如果在这个期间函数内部访问了外部的这个相同的内存将会报错,发生运行时的错误。
    2. 解决办法1是在函数外将这个内存的值复制一份给两一个变量,这样在函数内部访问这个变量即可,因为现在访问的将会是另一个内存。
    3. 解决办法2是在函数内部不访问外部的这个相同的内存,只是用传入的参数。
// 报错示例
var stepSize = 1
func increment(_ number: inout Int) {
     number = stepSize + number
}
increment(&stepSize)

// 修改方法1
var stepSize = 1
var size  = stepSize
func increment(_ number: inout Int) {
    number = size + number
}
increment(&stepSize)

// 修改方法2
var stepSize = 1
func increment(_ number: inout Int) {
    number = number + number
}
increment(&stepSize)
  1. 方法里self的访问冲突
func change(a: inout Int, b: inout Int){
    let sum = a + b
    a = sum/2
    b = sum - a
}

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

extension Player {
    mutating func shareHealth(player: inout Player) {
        change(a: &health, b: &player.health)
    }
}

var a = Player(name: "小明", health: 10, energy: 10)
var b = Player(name: "小红", health: 3, energy: 5)
a.shareHealth(player: &b)
print("a.health = \(a.health)   b.health = \(b.health)")

a.shareHealth(player: &a)//报错
  1. 属性的访问冲突
    1. 结构体属于值类型,对于结构体中的某个变量的访问其实要访问整个结构体。
    2. 如果一个方法要求传入的参数都是inout类型,并且都是一个结构体的两个不同的参数。那么就要对整个结构体进行访问。如果这个结构体是全局的,那么就是要对整个结构体进行长期写访问,就会报错。如果这个结构体是局部的,那么就是短期的写访问不报错。
func change(a: inout Int, b: inout Int){
    let sum = a + b
    a = sum/2
    b = sum - a
}

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

extension Player {
    mutating func shareHealth(player: inout Player) {
        change(a: &health, b: &player.health)
    }
}

//以下报错
//var a = Player(name: "小明", health: 10, energy: 10)
//change(a: &a.health, b: &a.energy)

//以下不报错
func test() {
    var a = Player(name: "小明", health: 10, energy: 10)
    change(a: &a.health, b: &a.energy)
}
  1. 总结
    1. 对于值类型
      1. 如结构体用let修饰以后。自身不可变,它中的所有变量变为不可变。
      2. 如果用var修饰以后,那么自身是可以变的,结构体中的变量常量依然不做变化。
    2. 对于引用类型
      1. 如果用let修饰,那么自身是不可以变的。它的所有属性不变。常量还是常量,变量还是变量。
      2. 如果用var修饰,那么自身是可以变的。它的所有属性不变。常量还是常量,变量还是变量。
    3. 方法参数
      0. 其实方法的参数默认给了let类型!!!
      1. 如果是值类型,如结构体,枚举,int,string。这时候默认是let修饰的。那么自身是不可变的,它中的所有属性也是常量不可变的。如果想变就要用inout进行修饰。可以理解为inout把let变为了var。
      2. 如果是引用类型,默认是let修饰的。那么它自身是不可变的。它中的所有属性保持不变。常量还是常量,变量还是变量。如果想要自身改变,那就用inout修饰
      3. 其实方法中的参数默认是let修饰的(隐藏起来了,对我们不可见),用inout修饰以后形参变为了var类型。
    4. 写访问
      1. inout类型的参数要进行写访问。如果传入这个参数是全局变量,就变成了对这个变量的长期写访问。在这个长期写访问期间,不能在进行访问这块内存,不然会报错。(但是可以通过局部变量的形式进行访问,不会报错)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值