Что означает амперсанд (&) на языке Swift?

Я знаю об амперсанде как о бит операции, но иногда вижу его перед именами переменных. Что делает & перед переменными?

Ответ 1

Он работает как inout, чтобы сделать переменную параметром in-out. In-out означает фактически передачу значения по ссылке, а не по значению. И это требует не только для принятия значения по ссылке, так и для передачи его по ссылке, поэтому передайте его с и - foo(&myVar) вместо foo(myVar)

Как вы видите, вы можете использовать это при передаче ошибок в Swift, где вам нужно создать ссылку на ошибку и передать ее функции, используя &, функция заполнит значение ошибки, если возникнет ошибка или передайте переменную обратно как это было до

Почему мы его используем? Иногда функция уже возвращает другие значения и просто возвращает другую (например, ошибку), будет путать, поэтому мы передаем ее как inout. В других случаях мы хотим, чтобы значения были заполнены функцией, поэтому нам не нужно перебирать множество возвращаемых значений, так как функция уже сделала это для нас - среди других возможных применений.

Я надеюсь, что это поможет!

Ответ 2

Это означает, что это переменная in-out. Вы можете сделать что-то прямо с этой переменной. Он передается по адресу, а не как копия.

Например:

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

Ответ 3

Если вы поместите & перед переменной в функцию, это означает, что эта переменная не имеет переменной.

@Икаро уже описал, что это значит, я просто приведу пример, чтобы проиллюстрировать разницу между inout-переменными и переменными:

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

Ответ 4

Как указано в других ответах, вы используете префикс & для передачи значения в параметр inout метода или вызова функции, как описано в Функции > Ярлыки аргументов функции и имена параметров > Параметры вывода на языке Swift Programming. Но там больше, чем это.

На практике вы можете думать о параметрах Swift inout и передавать им значения как похожие на C или С++ pass-by-address или pass-by-reference - и когда вы вызываете импортированные 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 }
}

(В "реальном" коде геттер и сеттер для x могут делать такие вещи, как принудительное использование минимальных/максимальных значений. Или x может выполнять другие трюки с вычисленными свойствами, например, разговаривать с базой данных SQL под капотом. Здесь мы просто обрабатываем вызов и получаем/устанавливаем базовое частное свойство.)

Теперь, что происходит, когда мы передаем 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/С++/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

..., что намного проще, чем разложение CGRect только для создания новой с другой координатой x.


Эта более тонкая версия поведения inout, называемая "результат по результату", и способы ее оптимизации до поведения "pass by address" в стиле C, рассматриваются в Объявления > Функции > Параметры вывода на языке Swift Programming.