将类或基元类型传递给函数时,函数中对参数所做的任何更改都将反映在类之外.这与inout
参数应该做的基本相同.
inout参数的一个好用例是什么?
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
修改传入参数的功能.
斯威夫特3+注:在斯威夫特3开始,该inout
关键字一定要来后,结肠和类型之前.例如,Swift 3+现在需要func changeChar(char: inout Character)
.
从Apple语言参考:声明 - 输入输出参数:
作为优化,当参数是存储在存储器中的物理地址处的值时,在函数体内部和外部使用相同的存储器位置.优化的行为称为引用调用; 它满足了拷入式拷贝模型的所有要求,同时消除了复制的开销.不要依赖于拷入式拷贝和参考呼叫之间的行为差异.
如果你有一个函数,它接受一个有些记忆明智的大值类型作为参数(比如,一个大的结构型),并返回相同类型,最后当函数返回始终只是用来代替主叫方的说法,则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语言指南:功能 - 输入输出参数.
有关inout
如何在内存中实际处理的详细信息(名称copy-in-copy-out
有点误导......)---除了上面语言指南的链接外 - 请参阅以下SO线程:
使用inout关键字:是通过引用传递的参数或通过copy-in copy-out(/按值结果调用)
(编辑补充:附加说明)
Lucas Huang上面接受的答案中给出的例子试图---在函数范围内使用inout
参数---访问作为inout
参数传递的变量.这是不推荐的,并明确警告不要在语言ref中做:
不要访问作为输入输出参数传递的值,即使原始参数在当前范围中可用.当函数返回时,对原始文件的更改将被副本的值覆盖.不要依赖于引用调用优化的实现来尝试防止更改被覆盖.
现在,在这种情况下,访问"仅"是不可变的,例如print(...)
,但是按照惯例,应该避免这样的所有访问.
根据评论者的要求,我将添加一个例子来阐明为什么我们不应该对"作为一个输入参数传递的价值"做任何事情.
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(而不是引用).我们通过重复语言参考文档中的以下声明来总结:
不要依赖于拷入式拷贝和参考呼叫之间的行为差异.
默认情况下,函数参数是常量.尝试从该函数体内更改函数参数的值会导致编译时错误.这意味着您无法错误地更改参数的值.如果希望函数修改参数的值,并且希望在函数调用结束后这些更改仍然存在,请将该参数定义为输入输出参数.