67

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

4

5 回答 5

85

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

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

我们为什么用它?有时一个函数已经返回了其他值,而仅仅返回另一个值(如错误)会令人困惑,因此我们将其作为 inout 传递。其他时候,我们希望这些值由函数填充,因此我们不必迭代大量返回值,因为函数已经为我们完成了 - 以及其他可能的用途。

希望对你有帮助!

于 2015-05-30T01:27:50.467 回答
17

这意味着它是一个输入输出变量。你可以直接用那个变量做一些事情。它是通过地址传递的,而不是作为副本传递的。

例如:

var temp = 10
func add(inout a: Int){
    a++
}
add(inout:&temp)
temp // 11
于 2015-05-30T01:25:48.703 回答
11

如果你把&一个变量放在一个函数之前,那意味着这个变量是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
于 2015-10-05T11:14:30.690 回答
11

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

protocol Foo {}
protocol Bar {}

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

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

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

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

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

于 2020-05-21T19:52:54.863 回答
4

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

在实践中,您可以将 Swiftinout参数和向它们传递值视为类似于 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 }
}

(在“真实”代码中,getter 和 setterx可以执行诸如强制最小/最大值之类的事情。或者x可以执行其他计算属性技巧,例如在后台与 SQL 数据库对话。这里我们只是检测调用和 get/设置底层私有属性。)

现在,当我们传递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参数“通过引用”传递它的工作方式与您期望的相同,如果它x是存储属性或局部变量。


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

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 { $0.title }) // -> ["window 2", "window 1"]

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

window.frame.origin.x += 10

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


这个更细微的inout行为版本,称为“按值结果调用”,以及它可以优化为 C 风格的“按地址传递”行为的方式,在Swift 编程语言中的声明 > 函数 > 输入输出参数中进行了介绍.

于 2016-09-19T22:02:14.837 回答