6

在以下代码中使用 swift inout 参数时,我感到有些失落:

var shouldContinue: Bool = true

func doSomeWork1(shouldContinue: inout Bool)
{
    while shouldContinue
    {
        // ERROR: the compiler wants: doSomeWork2(shouldContinue: &shouldContinue)
        doSomeWork2(shouldContinue: shouldContinue)
    }
}

func doSomeWork2(shouldContinue: inout Bool)
{
    while shouldContinue
    {

    }
}

为什么编译器想要doSomeWork2(shouldContinue: &shouldContinue)而不是the compiler wants: doSomeWork2(shouldContinue: shouldContinue)?还不shouldContinue是 doSomeWork1() 范围内的指针???

4

2 回答 2

10

作为指针只是 inout 参数优化过程的副作用。它们实际上使用复制输入复制输出行为以不同的方式工作。因此,在函数内部,该参数被视为常规变量,而不是指针。如果将它传递给另一个接受 inout 参数的函数,则必须将其标记为这样。

传入传出参数如下:

调用函数时,会复制参数的值。

在函数体中,副本被修改。

当函数返回时,副本的值被分配给原始参数。

这种行为称为复制输入复制输出或按值调用结果。例如,当计算属性或具有观察者的属性作为输入输出参数传递时,它的 getter 作为函数调用的一部分被调用,它的 setter 作为函数返回的一部分被调用。

作为优化,当参数是存储在内存中物理地址的值时,函数体内部和外部都使用相同的内存位置。优化的行为称为引用调用;它满足了copy-in-copy-out模型的所有要求,同时消除了复制的开销。使用由copy-in-copy-out给出的模型编写代码,而不依赖于引用调用优化,以便无论是否有优化它都能正确运行。

输入输出参数

于 2016-11-12T21:30:25.503 回答
1

来自:Matt Neuburg 书籍“iOS 13 Programming Fundamentals with Swift”。:

如果我们想要一个函数改变传递给它的参数的原始值,我们必须执行以下操作:

  • 我们要修改的参数类型必须声明为inout
  • 当我们调用函数时,保存要修改的值的变量必须用 var 声明,而不是 let。
  • 我们必须传递它的地址,而不是将变量作为参数传递。这是通过在其名称前加上与号 (&) 来完成的。

我们的 removeCharacter(_:from:) 现在看起来像这样:

 func removeCharacter(_ c:Character, from s: inout String) -> Int {
  var howMany = 0
  while let ix = s.firstIndex(of:c) {
    s.remove(at:ix)
    howMany += 1
  }
 return howMany
}

我们对 removeCharacter(_:from:) 的调用现在看起来像这样: var s = "hello" let result = removeCharacter("l", from:&s) 调用后,result 为 2,s 为 "heo"。当我们将它作为 from: 参数传递时,请注意名称 s 之前的 & 符号。它是必需的;如果你省略它,编译器会阻止你。我喜欢这个要求,因为它迫使我们明确地向编译器和我们自己承认,我们即将做一些有潜在危险的事情:作为副作用,我们让这个函数修改自身之外的值。

于 2019-10-16T22:21:39.523 回答