详细介绍下inout 参数的用法等,主要是对查看的官方文档的翻译及自己对相关知识点的理解的记录,怕以后忘记了😜
原文链接
以前在有些东西不清楚的时候也看过一些官方的文档,但是看过之后不久就忘记了,有想法想一点点的将以前看过的及以后看的官方文档都翻译过来记录一下,希望手不懒,能够实行下去🙏
In-Out Parameters 01
方法的参数默认是不可变(constants)类型(刚刚学习swift的时候还真的没注意这个)。在方法的内部视图去改变参数的值是会导致编译错误的(确实会报错,大家可以自己试一下)。
func changeStr(a: String, b: String) -> String {
a = "asd"
return a
}
如果想要在方法的内部去改变参数的值,并且想要在方法执行结束后仍然保持这个改变,那么可以用一个 in-out 参数替代原来的参数。
声明一个in-out参数的方法是放一个 inout 关键字在参数类型的前面。in-out参数会持有那个传递到方法内部的参数,当这个参数在方法内部被改变后,会将这个值传递出去替换原来的值。
你只能传递一个变量作为in-out参数(如果是let型则会报错)。你不能传递一个不可变(constant)的或者字面值作为参数,因为不可变量或者字面值是不能被更改的。当你将一个变量的名字作为in-out参数传递的时候,应该在这个变量名字前面加上&符号,以此用来表示这个变量的值可以被这个方法改变。
注意:in-out参数不能有默认值,同时可变参数也不能标记为in-out.
来看下面的例子,一个叫swapTwoInts(::)方法,他有两个in-out int型的参数 a 和 b:
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let tempA = a
a = b
b = tempA
}
swapTwoInts(::)方法只是简单的交换值,将b值分配给a,将a原来的值分配给b。这个方法的交换过程是将a的值存储在一个叫tempA的常量中,然后将b的值分配给a,在将tempA的值分配给b。
现在你可以通过调用swapTwoInts(::)方法,去改变两个作为参数的变量的。注意下面如果你想将someInt和anotherInt作为参数去,调用swapTwoInts(::)方法的时候,在参数的前面是有&前缀的
:
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
//现在 someInt = 107 anotherInt = 3
上面的例子展示了即使在swapTwoInts(::)方法外面定义的两个变量someInt 和 anotherInt,也能通过该方法去改变他们原有的值。
注意:in-out 参数和方法的返回值是不一样的。上面例子中的方法没有定义任何的返回值或者返回类型,但是依然可以改变方法外面的变量的值。in-out参数是方法对方法体外部产生影响的一种方式。
In-Out Parameters 02
In-out参数按照下面的过程被传递:
- 1 当方法被调用的时候,参数的值被拷贝
- 2 在方法体内部,被改变的是拷贝的值
- 3 当方法结束后,拷贝的值重新分配给原来的参数
这种行为就像考入,考出一样或者称为结果值调用。举个例子,当一个计算属性或者一个具有属性观察器的属性作为一个in-out参数被传递时,当方法被调用的时候,它的getter方法会被调用,当方法返回的时候,它的setter方法会被调用。
作为一种优化手段,当参数值被存储在内存物理地址中时,在方法内部和外部会用同一个内存位置。这种优化手段叫做引用调用。它满足了拷入拷出模式的所有要求,且消除了复制带来的开销。在代码中,要规范使用拷入拷出模式,不要依赖于引用调用。
不要使用传递给in-out参数的值,即使原始值在当前作用域中依然可用。当函数返回时,你对原始值所做的更改会被拷贝的值所覆盖。不要依赖于引用调用的优化机制来试图避免这种覆盖。
你不能将同一个值传递给不同的in-out参数,因为不好确定将哪一个拷贝值回写,因策原来的最终值㛑不好确定。
var x = 10
func f(a: inout Int, b: inout Int) {
a += 1
b += 1
}
f(a: &x, b: &x) //提示错误:Inout arguments are not allowed to alias each other
闭包或者嵌套函数中捕获的in-out参数一定是非逃逸的(nonescaping)。如果你需要捕获in-out参数而不去改变他的值,请使用捕获列表来显示的捕获参数
func someFunction(a: inout Int) -> () -> Int {
return { [a] in return a + 1 }
}
如果需要捕获和突变输入参数,请使用显式本地拷贝副本,例如多线程代码,以确保在函数返回之前已完成所有突变。
func multithreadedFunction(queue: DispatchQueue, x: inout Int) {
// Make a local copy and manually copy it back.
var localX = x
defer { x = localX }
// Operate on localX asynchronously, then wait before returning.
queue.async { someMutatingOperation(&localX) }
queue.sync {}
}
defer语句:特点是,当当前作用域代码执行结束后,要退出当前作用域时去执行defer块内的代码,同时如果有多个defer分代码块同时被定义,他将按照defer代码块定义的顺序反向执行
defer原文
希望我的文章对你有帮助,努力,坚持,与君共勉。
网友评论