将类或基元类型传递给函数时,函数中对参数所做的任何更改都将反映在类之外。这与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
函数,它将修改传入的参数。
Swift 3+注意:Starting in Swift 3,inout
关键字必须在冒号之后和类型之前。例如,Swift 3+现在需要func changeChar(char: inout Character)
。
来自Apple Language Reference: Declarations - In-Out Parameters:
作为优化,当参数是存储在存储器中的物理地址处的值时,在函数体内部和外部使用相同的存储器位置。优化的行为称为引用调用;它满足了拷入式拷贝模型的所有要求,同时消除了复制的开销。不要依赖于拷入式拷贝和参考呼叫之间的行为差异。
如果你有一个函数,它采用一个有点内存的大值类型作为参数(比如一个大的结构类型)并返回相同的类型,最后在函数返回总是用于替换调用者参数的地方,那么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(...)
,但按照惯例,所有这样的访问都应该避免。
根据评论者的要求,我将添加一个例子来阐明为什么我们不应该对“作为一个输入参数传递的价值”做任何事情。
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.
因此,在这种情况下,输入表现为copy-in-copy-out(而不是引用)。我们通过重复语言参考文档中的以下声明来总结:
不要依赖于拷入式拷贝和参考呼叫之间的行为差异。
inout参数允许我们更改值类型参数的数据,并在函数调用完成后保持更改。
如果你使用类,那么,正如你所说,你可以修改类,因为参数是对类的引用。但是当您的参数是值类型时,这将不起作用(https://docs.swift.org/swift-book/LanguageGuide/Functions.html - In-Out Parameters Section)
使用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
}
基本上,当您想要使用变量地址时,它非常有用,它在数据结构算法中非常有用
当使用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)")
}
}