inout 是 Swift 的一种参数修饰符,用于标记通过引用传递的参数。这意味着当将某个变量作为 inout 参数传递给函数时,该函数可以直接修改原始变量的值,而不仅仅是操作其副本。
特点
1、传递引用:inout 参数是通过变量的引用传递的,而不是值的副本。
2、变量会被修改:函数内部对 inout 参数的任何更改都会影响传入的原始变量。
3、使用 & 引用传递:调用函数时,需要在参数前加 & 符号,明确表示愿意让函数修改该变量。
语法
定义一个接受 inout 参数的函数:
func modifyValue(_ number: inout Int) {
number += 10
}
调用函数时:
var myNumber = 5
modifyValue(&myNumber) // 使用 & 表示引用传递
print(myNumber) // 输出: 15
在这个例子中:
myNumber 的初始值是 5。
modifyValue 函数接收 myNumber 的引用并在内部修改它。
调用后,myNumber 的值被修改为 15。
使用场景
1、模拟引用传递
在 Swift 中,函数参数默认是按值传递的。使用 inout 可以让函数直接操作传入的变量。
func swapValues(_ a: inout Int, _ b: inout Int) {
let temp = a
a = b
b = temp
}
var x = 3
var y = 7
swapValues(&x, &y)
print(x, y) // 输出: 7, 3
2、与 Objective-C 交互
在处理桥接的 Objective-C 方法(如 NSError ** 参数)时,inout 提供类似的行为。
注意事项
1、不能使用常量或字面量:inout 参数必须是可变的变量,不能是常量或字面量。
var value = 10
modifyValue(&value) // 正确
modifyValue(&10) // 错误:字面量不能作为 inout 参数
2、函数内部可以为参数重新赋值为新实例:inout 参数的引用地址可以在函数内改变,但其内容可以改变。
func reset(_ number: inout Int) {
number = 0 // 正确:修改内容
}
func invalidChange(_ number: inout Int) {
let anotherNumber = 20
number = anotherNumber // 合法地修改引用本身
}
3、避免不必要的使用:大多数情况下可以通过返回值来实现相同功能,只有在需要修改原始变量时才使用 inout。
inout 与 Objective-C 的桥接
在与 Objective-C 交互时,inout 是处理 NSError ** 的关键:
var error: NSError?
someObjectiveCFunction(&error) // 使用 & 传递引用
在这个例子中,Swift 的 &error 相当于将 NSError * 的地址传递给 Objective-C 方法,使其能够修改原始的 error 对象。
更深层次的理解:Swift 中的引用绑定
Swift 的 inout 参数是一种优化过的引用传递模式。传递变量时,Swift 会在函数内部操作一个引用,也允许改变它的指向。
理解引用和内容
对于引用类型(例如类实例),修改 inout 参数的属性是合法的,也可以重新赋值为一个新的对象。
修改内容
class Person {
var name: String
init(name: String) {
self.name = name
}
}
func changeName(_ person: inout Person) {
person.name = "Alice" // 修改引用指向对象的内容
}
var user = Person(name: "Bob")
changeName(&user)
print(user.name) // 输出: Alice
示意流程图:
改变引用
func reassignPerson(_ person: inout Person) {
let user2 = Person(name: "Alice") // 允许改变引用
person = user2
}
进一步验证:
因为修改后的user和user2地址相同,当修改user2的name时,user1也跟随改变。
总结
inout 参数允许函数直接修改传入的变量值。
在 Swift 中,它可以用来实现类似 C 和 Objective-C 中的引用传递。
调用时,需使用 & 明确传递变量的引用。