it-roy-ru.com

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

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

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

57
4thSpace

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

89
Lucas Huang

From Справочник по языку Apple: декларации - параметры входа :

Как оптимизация, когда аргумент является значением, хранящимся в физическом адрес в памяти, то же место памяти используется как внутри, так и вне функционального тела. Оптимизированное поведение называется вызовом ссылка; он удовлетворяет всем требованиям копирования модель копирования при удалении накладных расходов на копирование. Не зависит о поведенческих различиях между копированием-копированием и вызовом ссылка.

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

Рассмотрим пример ниже, где комментарии описывают, почему мы хотели бы использовать inout вместо обычной функции type-in-return-type:

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 может быть предпочтительна просто как хорошая практика кода, говорящая всем, кто читает наш код, что мы изменяем аргумент функции вызова (неявно показанный амперсандом &, предшествующим аргументу в вызов функции). Следующее суммирует это довольно аккуратно:

Если вы хотите, чтобы функция изменила значение параметра, и вы хотите чтобы эти изменения сохранялись после завершения вызова функции, определите этот параметр в качестве входного-выходного параметра вместо.

From Руководство по языку Apple: функции - параметры входа .


Подробную информацию о inout и о том, как он на самом деле обрабатывается в памяти (имя copy-in-copy-out несколько вводит в заблуждение ...) - в дополнение к ссылкам на руководство по языку выше - см. Следующую ветку SO:


(Изменить дополнение: дополнительная заметка)

Пример, приведенный в принятом ответе Лукаса Хуанга выше, пытается --- в области действия функции с использованием аргумента inout - получить доступ к переменным, которые были переданы в качестве аргументов inout. Это не рекомендуется и явно предупреждено не делать на языке ref:

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

Теперь доступ в этом случае является «только» неизменяемым, например, 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.myNotVeryThoughtThroughInoutFunction(&a.myStructsIntProperty)
print(a.myStructsIntProperty) // 3 or 4? prints 3.

Таким образом, в этом случае inout ведет себя как copy-in-copy-out (а не по ссылке). Подведем итоги, повторив следующее утверждение из языка ref docs:

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

32
dfri

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

 see image below for Description

17
Venu Gopal Tewari

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

0
H S Progr

при использовании inout параметра Swift 4.0 Work

class ViewController: UIViewController {

    var total:Int = 100

    override func viewDidLoad() {
        super.viewDidLoad()
        self.paramTotal(total1: &total)
    }

    func paramTotal(total1 :inout Int) {
        total1 = 111
        print("Total1 ==> \(total1)")
        print("Total ==> \(total)")
    }
}
0
user3263340

В основном это полезно, когда вы хотите играть с адресами переменных, это очень полезно в алгоритмах структуры данных

0
JeeVan TiWari

Если вы работаете с классами, то, как вы говорите, вы можете изменить класс, потому что параметр является ссылкой на класс. Но это не будет работать, если ваш параметр имеет тип значения ( https://docs.Swift.org/Swift-book/LanguageGuide/Functions.html - Раздел параметров In-Out)

Вот хороший пример использования inout (определение математики для CGPoints):

func + (left: CGPoint, right: CGPoint) -> CGPoint {
  return CGPoint(x: left.x + right.x, y: left.y + right.y)
}

func += (left: inout CGPoint, right: CGPoint) {
  left = left + right
}
0
abanet