美文网首页
26.内存安全

26.内存安全

作者: LucXion | 来源:发表于2021-07-28 09:39 被阅读0次

    内存访问的三个性质:读还是写,访问的内存地址,访问的时长

    只要包含以下情况中的任意两种,都会造成 访问冲突

    • 至少有一个是写访问
    • 它们访问的是同一个存储地址
    • 它们的访问在时间线上部分重叠
      • 重叠的访问主要出现在使用 in-out 参数的函数和方法或者结构体的 mutating 方法里

    In-Out 参数的访问冲突

    一个函数会对它所有的 in-out 参数进行长期写访问。in-out 参数的写访问会在所有非 in-out 参数处理完之后开始,直到函数执行完毕为止。如果有多个 in-out 参数,则写访问开始的顺序与参数的顺序一致。

    因为操作符也是函数,它们也会对 in-out 参数进行长期访问。例如,假设 changeTwo(_:_:) 是一个名为 <^> 的操作符函数,那么 num1 <^> num1 也会造成像 changeTwo(&num1, &num1)一样的冲突。

    var num1 = 1
    var num2 = 2
    func changeNum(with num:inout Int)->Int {
        return num + num1
    }
    print(changeNum(with:  &num1)) // 错误:changeNum 内部直接访问了 num1
    
    func changeTwo(num1:inout Int,num2:inout Int)->Int{
        return num1 + num2
    }
    changeTwo(num1: &num1, num2: &num1) // 错误:包含写访问,访问同一个内存地址,访问时间线重叠
    

    方法里 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)
        }
    }
    func balance(_ fromHealth:inout Int,_ toHealth:inout Int) {
        fromHealth = 1
        toHealth = 2
        print(fromHealth,toHealth)
    }
    
    var player = Player.init(name: "Li", health: 11, energy: 8)
    //player.shareHealth(with: &player)// 错误:shareHealth内隐式访问self,会造成访问同一个内存地址
    var player2 = Player.init(name: "Ming", health: 15, energy: 8)
    player.shareHealth(with: &player2) // 正确 
    

    属性的访问冲突

    var player = Player.init(name: "Li", health: 11, energy: 8)
    balance(&player.health, &player.energy) // 运行错误: 值类型,修改值的任何一部分都是对于整个值的修改,意味着其中一个属性的读或写访问都需要访问整一个值
    
    func someFunction (){
        var player2 = Player.init(name: "Ming", health: 15, energy: 8)
        balance(&player2.health, &player2.energy) // 运行正常:改为本地变量而非全局变量,编译器就会可以保证这个重叠访问是安全的
    

    限制结构体属性的重叠访问对于保证内存安全不是必要的。保证内存安全是必要的,但因为访问独占权的要求比内存安全还要更严格——意味着即使有些代码违反了访问独占权的原则,也是内存安全的,所以如果编译器可以保证这种非专属的访问是安全的,那 Swift 就会允许这种行为的代码运行。特别是当你遵循下面的原则时,它可以保证结构体属性的重叠访问是安全的:

    • 你访问的是实例的存储属性,而不是计算属性或类的属性
    • 结构体是本地变量的值,而非全局变量
    • 结构体要么没有被闭包捕获,要么只被非逃逸闭包捕获了

    如果编译器无法保证访问的安全性,它就不会允许那次访问。

    相关文章

      网友评论

          本文标题:26.内存安全

          本文链接:https://www.haomeiwen.com/subject/awiomltx.html