Подтвердить что ты не робот

Когда использовать inout параметры?

При передаче класса или примитивного типа в функцию любое изменение, внесенное в функцию в параметр, будет отражено вне класса. Это в основном то же самое, что и параметр inout.

Что такое хороший вариант использования inout-параметра?

4b9b3361

Ответ 1

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).

Ответ 2

Из Справочник по языку 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:

Не зависит от поведенческих различий между копированием в копии и вызов по ссылке.

Ответ 3

Функциональные параметры по умолчанию являются константами. Попытка изменить значение параметра функции из тела этой функции приводит к ошибке времени компиляции. Это означает, что вы не можете изменить значение параметра по ошибке. Если вы хотите, чтобы функция изменяла значение параметра, и вы хотите, чтобы эти изменения сохранялись после завершения вызова функции, определите этот параметр как параметр in-out.

см. изображение ниже для описания

Ответ 4

Используя параметр inout, можно изменить данные параметра типа значения и сохранить изменения после завершения вызова функции.