gpt4 book ai didi

swift - 我怎样才能防止这种逻辑错误? (var x 将其值复制到另一个var y 和使用var y 之间的变化,使y 过时)

转载 作者:行者123 更新时间:2023-11-28 08:00:53 28 4
gpt4 key购买 nike

总结:

我在我的 Swift 代码中犯了一个错误,我已经修复了它。然后我问自己为什么会这样,我该如何避免。我尝试了一些方法,但没有任何帮助。

我把错误和我的想法放在下面。我希望你能教我避免这种错误的正确方法,但任何想法、提示或建议都将不胜感激。


如何避免这种逻辑错误?

以下是我为斯坦福 cs193p 类(class)分配的摘录。

class SomeClass {

...

var accumulator: Double?

func someFunc() {
// I use `localAccumulator` and write `self.` for clarity.
if let localAccumulator = self.accumulator { // A
// `self.accumulator` is modified in this method
performPendingBinaryOperation() // B
pendingBinaryOperation = PendingBinaryOperation(firstOperand: localAccumulator) // C
}
}

private func performPendingBinaryOperation() {
accumulator = pendingBinaryOperation.perform(with: accumulator)
}

...

}

这里的问题是 B 行更改了实例值 self.accumulator 的值,而 C 行应该使用存储在 self.accumulator 中的新值, 但它使用从 self.accumulator 的旧值复制的过时的 本地变量 localAccumulator

通过调试器很容易找出逻辑错误。但后来我反省自己的错误,想寻找一种方法来避免这种逻辑错误。

方法一:使用nil检查而不是可选绑定(bind)

if self.accumulator != nil { // A
// `self.accumulator` is modified in this method
performPendingBinaryOperation() // B
pendingBinaryOperation = PendingBinaryOperation(firstOperand: self.accumulator!) // C
}

实际上,这里真正重要的是力解包 self.accumulator!,它确保值来自真实来源。使用 nil 检查而不是可选绑定(bind)可以强制我在 self.accumulator 上强制解包。

但在某些 Swift 风格指南(GitHub、RayWenderlich、LinkedIn)中,不鼓励强制展开。他们更喜欢可选绑定(bind)。

方法二:使用断言。

if localAccumulator = self.accumulator { // A
// `self.accumulator` is modified in this method
performPendingBinaryOperation() // B
assert(localAccumulator == self.accumulator) // D
pendingBinaryOperation = PendingBinaryOperation(firstOperand: localAccumulator) // C
}

我插入一个断言来检查 localAccumulator 是否仍然等于 self.accumulator。这是有效的,一旦 self.accumulator 被意外修改,它将停止运行。但是很容易忘记添加此断言行。

方法三:SwiftLint

为了找到检测此类错误的方法,我浏览了 SwiftLint 的所有规则,并对 SourceKitten (SwiftLint 的依赖项之一)。用 SwiftLint 检测这种错误似乎太复杂了,尤其是当我使这种模式更通用时。

一些类似的案例

案例一:守卫可选绑定(bind)

func someFunc() {
guard let localAccumulator = self.accumulator { // A
return
}
// `self.accumulator` is modified in this method
performPendingBinaryOperation() // B
pendingBinaryOperation = PendingBinaryOperation(firstOperand: localAccumulator) // C
}

在这种情况下,人类更难注意到错误,因为 localAccumulator 具有比可选绑定(bind)更广泛的守卫可选绑定(bind)范围。

案例二:函数传参导致的值拷贝

// Assume that this function will be called somewhere else with `self.accumulator` as its argument, like `someFunc(self.accumulator)`
func someFunc(_ localAccumulator) {
// `self.accumulator` is modified in this method
performPendingBinaryOperation() // B
pendingBinaryOperation = PendingBinaryOperation(firstOperand: localAccumulator) // C
}

在这种情况下,调用此函数时,localAccumulatorself.accumulator 复制,然后 self.accumulator 在行 B 中发生变化, C 行需要 self.accumulator 的新值,但从 localAccumulator 获取旧值。

其实基本的模式如下,

var x = oldValue
let y = x
functionChangingX() // assign x newValue
functionExpectingX(y) // expecting y is newValue, but it's oldValue

x ~ self.accumulator

y ~ localAccumulator

functionChangingX ~ performPendingBinaryOperation

functionExpectingX ~ PendingBinaryOperation.init

这个错误模式看起来很常见,我想应该给这个错误模式起个名字。

回到我的问题,我怎样才能避免这种逻辑错误?

最佳答案

这个例子展示了我从你的问题中得到的理解:

var name: String? = "Mike"

func doSomething() {

// Check if "name" is set (not nil)
if let personName = name {

addLastNameToGlobalName() // modifies global var "name"

//
// Here, you want to use the updated "name" value. As you mention, at this point,
// "name" could/might/should be different than "personName" (it could
// even be nil).
//
greet(person: ???) // use "name"? (which is optional), "personName"?, or what?

}
}

对我来说,一般方法是这里的问题。你是这样的事实:

  1. 检查全局可选值是否不为零
  2. 调用一个函数来改变这个全局可选值
  3. 想要使用更新后的全局可选值作为非可选值

问候这个人的方式/设计的简单改变可以让你避免你提到的“逻辑错误”类型。

示例 1:

var name: String? = "Mike"

func doSomething() {

// Check if "name" is set (not nil)
if let personName = name {
greet(person: fullName(for: personName))
}
}

示例 2:

var name: String? = "Mike"

func doSomething() {

// Check if "name" is set (not nil)
if let personName = name {
let fullName = addLastNameToGlobalName() // modifies global var "name" and returns the new value
greet(person: fullName)
}
}

关于swift - 我怎样才能防止这种逻辑错误? (var x 将其值复制到另一个var y 和使用var y 之间的变化,使y 过时),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46740780/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com