При передаче класса или примитивного типа в функцию любое изменение, внесенное в функцию в параметр, будет отражено вне класса. Это в основном то же самое, что и параметр inout
.
Что такое хороший вариант использования inout-параметра?
При передаче класса или примитивного типа в функцию любое изменение, внесенное в функцию в параметр, будет отражено вне класса. Это в основном то же самое, что и параметр inout
.
Что такое хороший вариант использования inout-параметра?
inout
означает, что изменение локальной переменной также изменит параметры передаваемых параметров. Без него параметры пройденного значения останутся одинаковыми. Попытка думать о ссылочном типе, когда вы используете inout
и тип значения, не используя его.
Например:
import UIKit
var num1: Int = 1
var char1: Character = "a"
func changeNumber(var num: Int) {
num = 2
print(num) // 2
print(num1) // 1
}
changeNumber(num1)
func changeChar(inout char: Character) {
char = "b"
print(char) // b
print(char1) // b
}
changeChar(&char1)
Хорошим вариантом использования будет функция swap
, которая изменит параметры переданного.
Swift 3+ Примечание: Начиная с Swift 3 ключевое слово inout
должно появиться после двоеточия и перед типом. Например, Swift 3+ теперь требует func changeChar(char: inout Character)
.
Из Справочник по языку Apple: объявления - параметры входа в систему:
В качестве оптимизации, когда аргумент представляет собой значение, хранящееся на физическом адрес в памяти, то же место памяти используется как внутри, так и внутри вне тела функции. Оптимизированное поведение называется вызовом Справка; он удовлетворяет всем требованиям копирования при удалении служебных данных копирования. Не зависеть о поведенческих различиях между копированием и копированием ссылка.
Если у вас есть функция, которая принимает несколько массивный тип значения в виде аргумента (например, большой тип структуры) и возвращает тот же тип, и, наконец, где функция return всегда используется только для замены аргумента вызывающего, тогда inout
следует использовать в качестве ассоциированного параметра функции.
Рассмотрим приведенный ниже пример, в котором комментарии описывают, почему мы хотели бы использовать inout
над регулярной функцией типа в обратном типе:
struct MyStruct {
private var myInt: Int = 1
// ... lots and lots of stored properties
mutating func increaseMyInt() {
myInt += 1
}
}
/* call to function _copies_ argument to function property 'myHugeStruct' (copy 1)
function property is mutated
function returns a copy of mutated property to caller (copy 2) */
func myFunc(var myHugeStruct: MyStruct) -> MyStruct {
myHugeStruct.increaseMyInt()
return myHugeStruct
}
/* call-by-reference, no value copy overhead due to inout opimization */
func myFuncWithLessCopyOverhead(inout myHugeStruct: MyStruct) {
myHugeStruct.increaseMyInt()
}
var a = MyStruct()
a = myFunc(a) // copy, copy: overhead
myFuncWithLessCopyOverhead(&a) // call by reference: no memory reallocation
Кроме того, в приведенном выше примере --- без учета проблем с памятью --- inout
может быть предпочтительнее просто хорошей практикой кода, рассказывающей, кто читает наш код, что мы мутируем аргумент вызывающего функцию (неявно показано амперсандом &
перед аргументом в вызове функции). Ниже приведено это довольно аккуратно:
Если вы хотите, чтобы функция изменяла значение параметра, и вы хотите эти изменения сохраняются после завершения вызова функции, определяют этот параметр вместо параметра out-out.
Из Руководство по языку Apple: функции - параметры входа в систему.
Подробнее о inout
и о том, как он фактически обрабатывается в памяти (имя copy-in-copy-out
несколько вводит в заблуждение...) --- в дополнение к ссылкам на руководство по языку выше - см. следующий поток SO:
(Изменить дополнение: Дополнительная заметка)
Пример, приведенный в принятом ответе Лукаса Хуана выше, пытается --- в рамках функции использовать аргумент inout
--- обращаться к переменным, которые были переданы в качестве аргументов inout
. Это не рекомендуется, и явным образом предупреждаю вас не делать на языке ref:
Не получать доступ к значению, которое было передано как аргумент in-out, даже если исходный аргумент доступен в текущей области. Когда функция возвращает, ваши изменения в оригинале перезаписываются значение копии. Не зависит от реализации оптимизация по запросу, чтобы попытаться сохранить изменения перезаписаны.
Теперь доступ в этом случае является "единственным", не изменяемым, например. print(...)
, но для этого доступа, как правило, следует избегать.
По просьбе комментатора я добавлю пример, чтобы осветить вопрос о том, почему мы не должны ничего делать с "значением, которое было передано как аргумент in-out".
struct MyStruct {
var myStructsIntProperty: Int = 1
mutating func myNotVeryThoughtThroughInoutFunction (inout myInt: Int) {
myStructsIntProperty += 1
/* What happens here? 'myInt' inout parameter is passed to this
function by argument 'myStructsIntProperty' from _this_ instance
of the MyStruct structure. Hence, we're trying to increase the
value of the inout argument. Since the swift docs describe inout
as a "call by reference" type as well as a "copy-in-copy-out"
method, this behaviour is somewhat undefined (at least avoidable).
After the function has been called: will the value of
myStructsIntProperty have been increased by 1 or 2? (answer: 1) */
myInt += 1
}
func myInoutFunction (inout myInt: Int) {
myInt += 1
}
}
var a = MyStruct()
print(a.myStructsIntProperty) // 1
a.myInoutFunction(&a.myStructsIntProperty)
print(a.myStructsIntProperty) // 2
a.myInoutFunction(&a.myStructsIntProperty)
print(a.myStructsIntProperty) // 3 or 4? prints 3.
Итак, в этом случае inout ведет себя как copy-in-copy-out (а не по ссылке). Мы суммируем, повторяя следующую формулировку на языке ref:
Не зависит от поведенческих различий между копированием в копии и вызов по ссылке.
Функциональные параметры по умолчанию являются константами. Попытка изменить значение параметра функции из тела этой функции приводит к ошибке времени компиляции. Это означает, что вы не можете изменить значение параметра по ошибке. Если вы хотите, чтобы функция изменяла значение параметра, и вы хотите, чтобы эти изменения сохранялись после завершения вызова функции, определите этот параметр как параметр in-out.
Используя параметр inout, можно изменить данные параметра типа значения и сохранить изменения после завершения вызова функции.