Является ли параметр swift inout переменной или указателем?
Я чувствую себя немного потерянным, используя параметр быстрого ввода в следующем коде:
var shouldContinue: Bool = true
func doSomeWork1(shouldContinue: inout Bool)
{
while shouldContinue
{
// ERROR: the compiler wants: doSomeWork2(shouldContinue: &shouldContinue)
doSomeWork2(shouldContinue: shouldContinue)
}
}
func doSomeWork2(shouldContinue: inout Bool)
{
while shouldContinue
{
}
}
Почему компилятор хочет doSomeWork2(shouldContinue: &shouldContinue)
вместо the compiler wants: doSomeWork2(shouldContinue: shouldContinue)
? не shouldContinue
уже указатель в области действия doSomeWork1()???
1 ответ
Указатель является лишь побочным эффектом процесса оптимизации для входных параметров. Они на самом деле работают по-разному, используя копирование-копирование. Так что внутри функции этот параметр обрабатывается как обычная переменная, а не указатель. Если вы передадите его другой функции, которая принимает параметр inout, вы должны пометить его как таковой.
Параметры In-Out передаются следующим образом:
Когда функция вызывается, значение аргумента копируется.
В теле функции копия модифицируется.
Когда функция возвращается, значение копии присваивается исходному аргументу.
Такое поведение известно как копирование-копирование или результат вызова по значению. Например, когда вычисляемое свойство или свойство с наблюдателями передается как параметр in-out, его метод get вызывается как часть вызова функции, а его метод set вызывается как часть возврата функции.
В качестве оптимизации, когда аргумент является значением, хранящимся по физическому адресу в памяти, одна и та же ячейка памяти используется как внутри, так и снаружи тела функции. Оптимизированное поведение называется вызовом по ссылке; он удовлетворяет всем требованиям модели копирования с копированием при удалении накладных расходов на копирование. Напишите свой код, используя модель, заданную методом copy-in-copy, вне зависимости от оптимизации по вызову, чтобы он работал правильно с оптимизацией или без нее.
Из: Мэтт Нойбург Книга "Основы программирования iOS 13 с помощью Swift".:
Если мы хотим, чтобы функция изменяла исходное значение переданного ей аргумента, мы должны сделать следующее:
- Тип параметра, который мы собираемся изменить, должен быть объявлен inout.
- Когда мы вызываем функцию, переменная, содержащая изменяемое значение, должна быть объявлена с помощью var, а не let.
- Вместо того, чтобы передавать переменную в качестве аргумента, мы должны передать ее адрес. Для этого перед его именем ставится амперсанд (&).
Наш removeCharacter(_:from:) теперь выглядит так:
func removeCharacter(_ c:Character, from s: inout String) -> Int {
var howMany = 0
while let ix = s.firstIndex(of:c) {
s.remove(at:ix)
howMany += 1
}
return howMany
}
И наш вызов removeCharacter(_:from:) теперь выглядит так: var s = "hello" let result = removeCharacter("l", from:&s) После вызова результат равен 2, а s равно "heo". Обратите внимание на амперсанд перед именем s, когда мы передаем его как аргумент from:. Требуется; если вы его опустите, компилятор остановит вас. Мне нравится это требование, потому что оно заставляет нас явно подтвердить компилятору и нам самим, что мы собираемся сделать что-то потенциально опасное: мы позволяем этой функции в качестве побочного эффекта изменять значение вне себя.