我知道&符号是一种位操作,但有时我会在变量名前面看到它。&
在变量前面放一个有什么作用?
5 回答
它的作用是inout
使变量成为输入输出参数。输入输出实际上意味着通过引用而不是按值传递值。它不仅需要通过引用接受值,还需要通过引用传递它,所以用 & 传递它 -foo(&myVar)
而不是仅仅foo(myVar)
如您所见,您可以在 Swift 中的错误处理中使用它,您必须创建一个错误引用并将其传递给函数&
,如果发生错误,该函数将填充错误值,或者像以前一样将变量传回
我们为什么用它?有时一个函数已经返回了其他值,而仅仅返回另一个值(如错误)会令人困惑,因此我们将其作为 inout 传递。其他时候,我们希望这些值由函数填充,因此我们不必迭代大量返回值,因为函数已经为我们完成了 - 以及其他可能的用途。
希望对你有帮助!
这意味着它是一个输入输出变量。你可以直接用那个变量做一些事情。它是通过地址传递的,而不是作为副本传递的。
例如:
var temp = 10
func add(inout a: Int){
a++
}
add(inout:&temp)
temp // 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
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 之前的代码中看到这种语法。
如其他答案中所述,您使用前缀&
将值传递给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 编程语言中的声明 > 函数 > 输入输出参数中进行了介绍.