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”的访问重叠,但修改需要独占访问;考虑复制到局部变量
QuestionOne : 好的,但是MARK2部分呢?Swift 编译器进行了一些优化来解决这里对 struct 的写访问?
苹果解释如下解决方案,但我不清楚。
编译器可以证明保留了内存安全性,因为这两个存储的属性不会以任何方式交互。
问题二:通过在此处生成全局运行时问题解决。但我想知道怎么做?还有为什么它不适用于 MARK1 部分,为什么?
PS:苹果文档在这里
解决方案
使用robGlobal.someFunction()
,您正在调用shareHealth
的单独本地实例robLocal
。显然,在这种情况下不存在内存安全问题。
但是使用robGlobal.anotherFunction()
,然后您调用 的实例方法robGlobal
,然后将另一个引用传递给相同robGlobal
的shareHealth
,因此违反内存安全。
但这很好。这在功能上等同于内存安全检查保护您免受的其他尝试之一:
robGlobal.shareHealth(with: &robGlobal)
整个想法是与另一个玩家分享健康。撇开内存安全检查不谈,与自己分享一个玩家的健康意味着什么?例如,您当前的健康状况是 11。然后您与自己“分享”它。那么,您现在的健康状况是 5 吗?还是6?你的健康值是 11 岁,而现在你与自己分享了它之后,它现在是一个较小的值,这是否有意义?与自己分享的整个想法是没有意义的。“内存安全”检查正在保护您免受此类滥用。
就个人而言,我会
创建
balance
一个static
方法,因为它所做的只是“平衡”两个整数值,而不是对当前实例做任何事情;这也应该是私有的,因为不需要公开这个函数;和
由于您似乎想要“共享健康”以平衡两个玩家之间的健康以及平衡给定玩家的健康和能量,我会编写这两种方法。
因此,我们最终得到类似:
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)
}
这个新界面(shareHealth
和balanceHeathAndEnergy
)简化了界面。您可以在本地变量或属性(或全局变量,如果您真的想要的话;但拥有全局变量通常是个坏主意)上调用它们。但是你永远不会shareHealth
和自己在一起,现在这balance
是私人的,它最大限度地减少了你问题中概述的那种滥用。
FWIW,我不会让这些测试方法成为实例方法,Player
因为它们没有对当前实例做任何事情。您可以将它们设为静态方法。或者你可以在你的模型(或测试或其他)中让它们成为方法。Player
但它们作为实例方法没有意义。
推荐阅读
- javascript - 定期刷新页面时延迟滚动页面到上一个位置
- sql - 从两个不同的表中减去列的值
- node.js - 通过 Cloud Function 访问 Firestore
- heroku - 部署后如何在 Heroku 中运行迁移?
- google-app-engine - 如何解决 google-cloud app-engine 灵活环境配额问题
- java - 在终端会话中更改 Java 版本 (Mac OSX)
- sql-server - 在 SQL Server 2017 Management Studio 中计划作业
- javascript - 从对象数组中返回包含特定参数的随机对象
- css - 如何为打字稿中的元素设置样式?(Angular)
- python-3.x - 无法在 pandas 数据框中读取 glove.6B.300d.txt