由于两个存储的属性在 Swift 中不以任何方式交互,因此如何保持内存安全?

Dra*_*ner 5 swift

import UIKit

var robGlobal = Player(name: "Rob", health: 10, energy: 10)
var matt = Player(name: "Matt", health: 5, energy: 10)


struct Player {
    var name: String
    var health: Int
    var energy: Int
    
    static let maxHealth = 10
    mutating func restoreHealth() {
        health = Player.maxHealth
    }
    
    func balance(_ x: inout Int, _ y: inout Int) {
        let sum = x + y
        x = sum / 2
        y = sum - x
    }
    
}

class ViewController: UIViewController {
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        
        //MARK1: -Conflicting Access to self in Methods
        
        robGlobal.shareHealth(with: &matt)
        
        robGlobal.shareHealth(with: &robGlobal) // NOT OK in compile time Error: //Overlapping accesses to 'robGlobal', but modification requires exclusive access; consider copying to a local variable
        
        var robNewLocal = Player(name: "Rob", health: 10, energy: 10)
        
        robNewLocal.shareHealth(with: &robNewLocal) // NOT OK in compile time
        
        
        //MARK2: -Conflicting Access to properties in Methods
        
        robGlobal.someFunction()  // OK
        
        robGlobal.anotherFunction() // NOT OK in Run Time Error: Thread 1: Simultaneous accesses to 0x108cc4050, but modification requires exclusive access
        
    }
}


extension Player {
    mutating func shareHealth(with teammate: inout Player) {
        balance(&teammate.health, &health)
        
    }
    func someFunction() {
        var robLocal = Player(name: "Rob", health: 10, energy: 10)
        balance(&robLocal.health, &robLocal.energy)  // OK
    }
    
    func anotherFunction() {
        balance(&robGlobal.health, &robGlobal.energy)  // NOT OK in Run Time
    }
}
Run Code Online (Sandbox Code Playgroud)

我试图了解 Swift 中的内存安全,我在两种情况之间感到困惑。

正如我在方法部分标记的MARK1: -Conflicting Access to self是有道理的。两次写访问相互重叠。在运行之前编译器已经给了我错误:

'robGlobal' 的重叠访问,但修改需要独占访问;考虑复制到局部变量


问题一:好的,但是MARK2部分呢?Swift 编译器进行了一些优化来解决这里对 struct 的写访问?

苹果解释说明如下解决方案,但我不清楚。

编译器可以证明保留了内存安全,因为这两个存储的属性不会以任何方式交互。

问题二:通过使全局运行时问题解决。但我想知道如何?还有为什么它不适用于 MARK1 部分,为什么?

PS:苹果文档在这里

Rob*_*Rob 1

使用robGlobal.someFunction(),您可以调用 的shareHealth单独本地实例robLocal。显然在这种情况下不存在内存安全问题。

\n

但是对于robGlobal.anotherFunction(),您将调用 的实例方法robGlobal,但随后传递另一个对相同的引用robGlobalshareHealth,因此内存安全违规。

\n

但这很好。这在功能上相当于内存安全检查保护您免受的其他尝试之一:

\n
robGlobal.shareHealth(with: &robGlobal) \n
Run Code Online (Sandbox Code Playgroud)\n

整个想法是与其他玩家分享健康状况。抛开内存安全检查不谈,与自己共享一个玩家\xe2\x80\x99s 生命值意味着什么?例如,您当前的健康状况是 11。然后您与自己 \xe2\x80\x9cshare\xe2\x80\x9d 一起使用。那么,您现在的健康状况是 5 吗?还是6?您的健康状况是 11,而在您与自己分享后,它现在的值变小了,这是否有意义?与自己分享的整个想法没有意义。\xe2\x80\x9c 内存安全\xe2\x80\x9d 检查可保护您免受此类滥用。

\n
\n

就我个人而言,我会

\n
    \n
  • 创建balance一个static方法,因为它只是执行 \xe2\x80\x9cbalancing\xe2\x80\x9d 两个整数值,而不对当前实例执行任何操作;

    \n
  • \n
  • 这也可能应该是私有的,因为不需要公开这个函数;和

    \n
  • \n
  • 因为您似乎想要 \xe2\x80\x9cshare health\xe2\x80\x9d 来平衡两个玩家之间的健康以及平衡给定玩家的健康和能量,所以我会编写这两个方法。

    \n
  • \n
\n

因此,我们最终得到如下结果:

\n
robGlobal.shareHealth(with: &robGlobal) \n
Run Code Online (Sandbox Code Playgroud)\n

然后你可以做类似的事情:

\n
struct Player {\n    var name: String\n    var health: Int\n    var energy: Int\n\n    static let maxHealth = 10\n}\n\n// MARK: - Interface\n\nextension Player {\n    mutating func restoreHealth() {\n        health = Player.maxHealth\n    }\n\n    mutating func shareHealth(with teammate: inout Player) {\n        Self.balance(&teammate.health, &health)\n    }\n\n    mutating func balanceHealthAndEnergy() {\n        Self.balance(&health, &energy)\n    }\n}\n\n// MARK: - Private utility methods\n\nprivate extension Player {\n    static func balance(_ x: inout Int, _ y: inout Int) {\n        let sum = x + y\n        x = sum / 2\n        y = sum - x\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这个新界面(shareHealthbalanceHeathAndEnergy)简化了界面。您可以在本地变量或属性(或全局变量,如果您确实愿意的话)上调用它们;但使用全局变量通常是一个坏主意。但你永远不会shareHealth和自己在一起,现在这balance是私人的,它可以最大限度地减少你问题中概述的滥用。

\n

FWIW,我不会将这些测试方法作为实例方法,Player因为它们不会对当前实例执行任何操作。您可以将它们设为静态方法。或者你可以在你的模型(或测试或其他什么)中让它们成为方法。Player但它们作为实例方法没有意义。

\n