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 Parameters 在 Swift 编程语言 中。
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 之前的代码中看到此语法。
我知道和号是一种位运算,但有时我会在变量名前看到它。将 &
放在变量前面有什么作用?
表示是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 Parameters 在 Swift 编程语言 中。
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 之前的代码中看到此语法。