Swift 语言中的符号 (&) 是什么意思?

What does an ampersand (&) mean in the Swift language?

我知道和号是一种位运算,但有时我会在变量名前看到它。将 & 放在变量前面有什么作用?

表示是in-out变量。您可以直接使用该变量执行某些操作。它是通过地址传递的,而不是作为副本传递的。

例如:

var temp = 10
func add(inout a: Int){
    a++
}
add(inout:&temp)
temp // 11

它作为 inout 使变量成为输入输出参数。 In-out实际上意味着通过引用传递值,而不是通过值传递值。它不仅需要通过引用接受值,还需要通过引用传递它,所以用 & - foo(&myVar) 而不是 foo(myVar)

传递它

如您所见,您可以在 Swift 中的错误处理中使用它,您必须在其中创建错误引用并将其传递给使用 & 的函数,如果发生错误或将变量传回原样

我们为什么要使用它?有时一个函数已经 returns 其他值并且只是 returning 另一个值(如错误)会令人困惑,因此我们将其作为 inout 传递。其他时候我们希望值由函数填充,这样我们就不必迭代很多 return 值,因为函数已经为我们完成了 - 除了其他可能的用途。

希望对你有所帮助!

如果你在函数中把&放在一个变量之前,这意味着这个变量是inout变量。

@Icaro已经说明什么意思了,我就举个例子来说明inout变量和in变量的区别:

func majec(inout xValue:Int, var yValue:Int) {
    xValue = 100
    yValue = 200
}

var xValue = 33
var yValue = 33
majec(&xValue, yValue: yValue)
xValue //100
yValue //33

如其他答案所述,您使用前缀 & 将值传递给方法或函数调用的 inout 参数,如 [=54 中的 Functions > Function Argument Labels and Parameter Names > In-Out Parameters 中所述=]Swift 编程语言。但不仅如此。

在实践中,您可以考虑 Swift inout 参数并将值传递给它们类似于 C 或 C++ 按地址传递或按引用传递。事实上,编译器会将 inout 参数的许多使用优化为大致相同的机制(尤其是当您调用处理指针的导入 C 或 ObjC API 时)。然而,这些只是优化——在语义层面上,inout 确实不会四处传递地址,这让编译器解放出来,使这种语言结构更加灵活和强大。

例如,这里有一个结构使用通用策略来验证对其属性之一的访问:

struct Point {
    private var _x: Int
    var x: Int {
        get {
            print("get x: \(_x)")
            return _x
        }
        set {
            print("set x: \(newValue)")
            _x = newValue
        }
    }
    // ... same for y ...
    init(x: Int, y: Int) { self._x = x; self._y = y }
}

(在 "real" 代码中,x 的 getter 和 setter 可以执行诸如强制执行 minimum/maximum 值之类的操作。或者 x可以做其他计算-属性 技巧,比如在幕后与 SQL 数据库交谈。这里我们只是检测调用和 get/set 底层私有 属性。)

现在,当我们将 x 传递给 inout 参数时会发生什么?

func plusOne(num: inout Int) {
    num += 1
}

var pt = Point(x: 0, y: 1)
plusOne(num: &pt.x)
// prints:
//   get x: 0
//   set x: 1

因此,即使 x 是计算的 属性,使用 inout 参数传递它 "by reference" 的效果与您预期的相同,如果 x 是存储的 属性 或局部变量。


这意味着您可以传递各种 "by reference" 在 C/C++/ObjC 中甚至无法考虑的东西。例如,考虑标准库 swap 函数,它接受任意两个... "things" 并切换它们的值:

var a = 1, b = 2
swap(&a, &b)
print(a, b) // -> 2 1

var dict = [ "Malcolm": "Captain", "Kaylee": "Mechanic" ]
swap(&dict["Malcolm"], &dict["Kaylee"])
print(dict) // -> ["Kaylee": "Captain", "Malcolm": "Mechanic"], fanfic ahoy

let window1 = NSWindow()
let window2 = NSWindow()
window1.title = "window 1"
window2.title = "window 2"
var windows = [window1, window2]
swap(&windows[0], &windows[1])
print(windows.map { [=12=].title }) // -> ["window 2", "window 1"]

inout 的工作方式还让您可以做一些有趣的事情,例如在嵌套调用链上使用 += 运算符:

window.frame.origin.x += 10

... 这比分解 CGRect 只是为了构造一个具有不同 x 坐标的新的要简单得多。


这个更细微的 inout 行为版本,称为 "call by value result",以及它可以优化到 C 风格 "pass by address" 行为的方法,在 Declarations > Functions > In-Out ParametersSwift 编程语言 中。

Swift 语言中的符号还有一个功能尚未提及。举个例子:

protocol Foo {}
protocol Bar {}

func myMethod(myVar: Foo & Bar) {
    // Do something
}

这里的 & 符号语法说明 myVar 符合 Foo 和 Bar 协议。

作为另一个用例,请考虑以下内容:

func myMethod() -> UIViewController & UITableViewDataSource {
    // Do something
}

这里我们说方法 returns 一个 class 实例(UIViewController 的)符合特定协议 (UITableViewDataSource)。这在 Swift 5.1 的 Opaque Types 中有些过时,但您可能会不时在 Swift 5.1 之前的代码中看到此语法。