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

标签 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
    }
}
我试图了解 Swift 中的内存安全,我在两种情况之间感到困惑。
正如我标记的 MARK1:-方法中对自身的访问冲突节是有道理的。两次写访问相互重叠。在运行之前编译器已经给了我错误:
'robGlobal' 的重叠访问,但修改需要独占访问;考虑复制到局部变量

问题一 : 好的,但是 怎么样? MARK2 部分? Swift 编译器进行了一些优化来解决这里对 struct 的写访问?
苹果解释说明如下解决方案,但我不清楚。

The compiler can prove that memory safety is preserved because the two stored properties don’t interact in any way.


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

最佳答案

robGlobal.someFunction() ,您正在拨打 shareHealthrobLocal 的单独本地实例上.显然,在这种情况下不存在内存安全问题。
但与 robGlobal.anotherFunction() ,然后您将调用 robGlobal 的实例方法,但随后又传递了对相同 robGlobal 的另一个引用至 shareHealth ,因此违反了内存安全。
但这很好。这在功能上等同于内存安全检查保护您免受的其他尝试之一:

robGlobal.shareHealth(with: &robGlobal) 
整个想法是与另一个玩家分享健康。撇开内存安全检查不谈,与自己分享一个玩家的健康意味着什么?例如。您当前的健康状况是 11。然后您与自己“分享”它。那么,您现在的健康状况是 5 吗?还是6?你的健康值是​​ 11 并且在你与自己分享之后它现在是一个较小的值是否有意义?与自己分享的整个想法是没有意义的。 “内存安全”检查可保护您免受此类滥用。

就个人而言,我会
  • 制作 balance static方法,因为它只是在“平衡”两个整数值,而不是对当前实例做任何事情;
  • 这也应该是私有(private)的,因为不需要公开这个函数;和
  • 由于您似乎想要“共享健康”以平衡两个玩家之间的健康以及平衡给定玩家的健康和能量,因此我将编写这两种方法。

  • 因此,我们最终得到如下结果:
    struct Player {
        var name: String
        var health: Int
        var energy: Int
    
        static let maxHealth = 10
    }
    
    // MARK: - Interface
    
    extension Player {
        mutating func restoreHealth() {
            health = Player.maxHealth
        }
    
        mutating func shareHealth(with teammate: inout Player) {
            Self.balance(&teammate.health, &health)
        }
    
        mutating func balanceHealthAndEnergy() {
            Self.balance(&health, &energy)
        }
    }
    
    // MARK: - Private utility methods
    
    private extension Player {
        static func balance(_ x: inout Int, _ y: inout Int) {
            let sum = x + y
            x = sum / 2
            y = sum - x
        }
    }
    
    然后你可以做这样的事情:
    func someFunction() {
        var rob = Player(name: "Rob", health: 20, energy: 10)
        rob.balanceHealthAndEnergy()
    }
    
    func anotherFunction() {
        var rob = Player(name: "Rob", health: 10, energy: 10)
        var matt = Player(name: "Matt", health: 5, energy: 10)
    
        rob.shareHealth(with: &matt)
    }
    
    这个新界面( shareHealthbalanceHeathAndEnergy )简化了界面。您可以在本地变量或属性上调用它们(或全局变量,如果您真的想要;但使用全局变量通常是一个坏主意)。但你永远不会shareHealth与你自己,现在,balance是私密的,它最大限度地减少了您问题中概述的那种滥用。
    FWIW,我不会让这些测试方法成为Player 的实例方法。因为他们没有对当前实例做任何事情。您可以使它们成为静态方法。或者你可以在你的模型(或测试或其他)中创建它们的方法。但它们没有意义,因为 Player实例方法。

    关于swift - 由于两个存储的属性在 Swift 中不以任何方式交互,因此如何保持内存安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67255923/

    相关文章:

    swift - 同时将菜单项连接到第一响应者和多个 View Controller

    Swift Firebase - 将数据库快照转换为数组

    json - Swift:有办法快速调试可解码对象

    ios - 如何将主标签栏中的图像背景传递或传输到另一个标签栏 Swift 2

    ios - 无法在 iOS Swift 中获取 SecKey UnsafeMutablePointer

    ios - TableView不显示数据

    ios - SWIFT 中的元组数组作为函数的参数

    ios - 向 UIView 添加圆角会禁用 touchesBegan

    ios - iOS后台调优后单例实例的生命周期

    swift - swift 中的下标和协议(protocol)