何时使用inout参数?

时间:2015-12-27 23:40:13

标签: swift swift2

将类或基元类型传递给函数时,函数中对参数所做的任何更改都将反映在类之外。这与inout参数应该完全相同。

inout参数的一个好用例是什么?

8 个答案:

答案 0 :(得分:97)

inout表示修改局部变量也会修改传入的参数。没有它,传入的参数将保持相同的值。在使用inout和值类型而不使用它时,尝试考虑引用类型。

例如:

import UIKit

var num1: Int = 1
var char1: Character = "a"

func changeNumber(var num: Int) {
    num = 2
    print(num) // 2
    print(num1) // 1
}
changeNumber(num1)

func changeChar(inout char: Character) {
    char = "b"
    print(char) // b
    print(char1) // b
}
changeChar(&char1)

一个好的用例将是swap函数,它将修改传入的参数。

Swift 3+注意Starting in Swift 3inout关键字必须在冒号之后和类型之前。例如,Swift 3+现在需要func changeChar(char: inout Character)

答案 1 :(得分:35)

来自Apple Language Reference: Declarations - In-Out Parameters

  

作为优化,当参数是存储在物理上的值时   在内存中的地址,内部和内部都使用相同的内存位置   功能体外。优化的行为称为call by   参考;它满足了复制的所有要求   在删除复制开销的同时删除模型 。不依赖   关于拷入式拷贝和呼叫的行为差异   参考

如果你有一个函数,它采用一些内存方式的大值类型作为参数(比如一个大型结构类型)并返回相同的类型,最后总是使用函数返回来替换调用者参数,然后inout更喜欢作为相关的函数参数。

考虑下面的示例,其中注释描述了为什么我们希望在常规类型返回类型函数中使用inout

struct MyStruct {
    private var myInt: Int = 1

    // ... lots and lots of stored properties

    mutating func increaseMyInt() {
        myInt += 1
    }
}

/* call to function _copies_ argument to function property 'myHugeStruct' (copy 1)
   function property is mutated
   function returns a copy of mutated property to caller (copy 2) */
func myFunc(var myHugeStruct: MyStruct) -> MyStruct {
    myHugeStruct.increaseMyInt()
    return myHugeStruct
}

/* call-by-reference, no value copy overhead due to inout opimization */
func myFuncWithLessCopyOverhead(inout myHugeStruct: MyStruct) {
    myHugeStruct.increaseMyInt()
}

var a = MyStruct()
a = myFunc(a) // copy, copy: overhead
myFuncWithLessCopyOverhead(&a) // call by reference: no memory reallocation

此外,在上面的示例中---无视内存问题--- inout可以作为一个良好的代码实践,告诉任何人读取我们的代码,我们正在改变函数调用者参数(隐式显示为函数调用中参数前面的&符号&。以下总结了这一点:

  

如果您希望函数修改参数的值,并且您想要   这些更改在函数调用结束后仍然存在,定义   而是将该参数作为输入输出参数。

来自Apple Language Guide: Functions - In-Out Parameters

有关inout及其在内存中实际处理方式的详细信息(名称copy-in-copy-out有点误导......)---除了上述语言指南的链接外 - 请参阅跟随SO线程:

(编辑补充:附加说明)

上面Lucas Huang接受的答案中给出的例子试图---在使用inout参数的函数范围内---访问作为inout参数传递的变量。建议不要这样做,并明确警告不要使用语言ref:

  

不要访问作为输入输出参数传递的值,即使是   原始参数在当前范围内可用。当。。。的时候   函数返回,您对原始的更改将被覆盖   副本的价值。 不依赖于执行   调用引用优化以尝试保持更改不存在   覆盖

现在,在这种情况下,访问“仅”是不可变的,例如print(...),但按照惯例,应避免这样的所有访问。

根据评论者的要求,我将添加一个示例来阐明为什么我们不应该对“作为输入参数传递的值做任何事情。” / p>

struct MyStruct {
    var myStructsIntProperty: Int = 1

    mutating func myNotVeryThoughtThroughInoutFunction (inout myInt: Int) {
        myStructsIntProperty += 1
        /* What happens here? 'myInt' inout parameter is passed to this
           function by argument 'myStructsIntProperty' from _this_ instance
           of the MyStruct structure. Hence, we're trying to increase the
           value of the inout argument. Since the swift docs describe inout 
           as a "call by reference" type as well as a "copy-in-copy-out"
           method, this behaviour is somewhat undefined (at least avoidable).

           After the function has been called: will the value of
           myStructsIntProperty have been increased by 1 or 2? (answer: 1) */
        myInt += 1
    }

    func myInoutFunction (inout myInt: Int) {
        myInt += 1
    }
}

var a = MyStruct()
print(a.myStructsIntProperty) // 1
a.myInoutFunction(&a.myStructsIntProperty)
print(a.myStructsIntProperty) // 2
a.myNotVeryThoughtThroughInoutFunction(&a.myStructsIntProperty)
print(a.myStructsIntProperty) // 3 or 4? prints 3.

因此,在这种情况下,inout表现为copy-in-copy-out(而不是引用)。我们通过重复语言参考文档中的以下声明来总结:

  

不要依赖于copy-in copy-out之间的行为差​​异   并通过参考电话。

答案 2 :(得分:19)

默认情况下,函数参数是常量。尝试从该函数体内更改函数参数的值会导致编译时错误。这意味着您无法错误地更改参数的值。如果您希望函数修改参数的值,并且希望在函数调用结束后这些更改仍然存在,请将该参数定义为输入输出参数。

see image below for Description

答案 3 :(得分:0)

inout参数允许我们更改值类型参数的数据,并在函数调用完成后保持更改。

答案 4 :(得分:0)

如果您使用类,那么就可以说,您可以修改类,因为参数是对类的引用。但这在您的参数是值类型(https://docs.swift.org/swift-book/LanguageGuide/Functions.html-“进出参数”部分)时不起作用

使用inout的一个很好的例子是这个(为CGPoints定义数学):

func + (left: CGPoint, right: CGPoint) -> CGPoint {
  return CGPoint(x: left.x + right.x, y: left.y + right.y)
}

func += (left: inout CGPoint, right: CGPoint) {
  left = left + right
}

答案 5 :(得分:0)

基本上,当您要使用变量地址时,它很有用。它在数据结构算法中非常有用

答案 6 :(得分:0)

使用inout参数swift 4.0 Work

class ViewController: UIViewController {

    var total:Int = 100

    override func viewDidLoad() {
        super.viewDidLoad()
        self.paramTotal(total1: &total)
    }

    func paramTotal(total1 :inout Int) {
        total1 = 111
        print("Total1 ==> \(total1)")
        print("Total ==> \(total)")
    }
}

答案 7 :(得分:0)

输入参数:修改传递值和局部变量值。

func doubleInPlace(number: inout Int) {
     number *= 2
     print(number) 
    
 }

 var myNum = 10 doubleInPlace(number: &myNum)