При передачі класу або примітивного типу у функцію будь-які зміни, внесені у функцію до параметра, відображатимуться поза класом. В основному це те саме, що 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)
.
inout
копіюється назад, коли функція повертається. Тож char1
вже має бути a
в межах функції?
inout
мові. Я додаю приклад у свою відповідь.
var
у changeNumber
наступних версіях Swift ваша функція буде застарілою.
З довідкової мови Apple: Декларації - параметри входу-виходу :
В якості оптимізації, коли аргументом є значення, що зберігається за фізичною адресою в пам'яті, одне і те ж розташування пам'яті використовується як усередині, так і поза тілом функції. Оптимізована поведінка відома як виклик за допомогою посилання; він задовольняє всім вимогам моделі копіювання одночасно видаляючи накладні витрати на копіювання . Не залежать від поведінкових відмінностей між копіюванням та викликом за посиланням.
Якщо у вас є функція , яка приймає кілька пам'ятей мудрі велике значення типу в якості аргументу (скажімо, великий тип структури) і повертає той же тип, і , нарешті , де функція повернення завжди використовується тільки для заміни аргументу абонента, то 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
можна віддати перевагу просто як хороша практика коду, повідомляючи кожному, хто читає наш код, що ми мутуємо аргумент виклику функції (неявно показано амперсандом&
перед аргументом у функції дзвінок). Далі досить акуратно це викладено:
Якщо ви хочете, щоб функція модифікувала значення параметра, і ви хочете, щоб ці зміни зберігалися і після закінчення виклику функції, визначте цей параметр як параметр виходу.
З мовного довідника Apple: Функції - параметри виходу .
Для отримання детальної інформації щодо inout
того, як це насправді обробляється в пам'яті (назва copy-in-copy-out
дещо вводить в оману ...) --- додатково до посилань на мовний посібник вище --- див.
(Редагувати додаток: Додаткова примітка)
Приклад, наведений у прийнятій відповіді Лукаса Хуанга вище, намагається --- у межах функції з використанням inout
аргументу --- отримати доступ до змінних, переданих як inout
аргументи. Це не рекомендується, і явно попереджається не робити цього в мові ref:
Не отримуйте доступ до значення, яке було передано як аргумент in-out, навіть якщо оригінальний аргумент доступний у поточній області . Коли функція повертається, ваші зміни в оригіналі замінюються значенням копії. Не залежать від реалізації оптимізації виклику за посиланням, щоб намагатися уникнути перезапису змін .
Тепер доступ у цьому випадку є "лише" незмінним, наприклад print(...)
, але такого доступу слід, за домовленістю, уникати.
На прохання коментатора я додаю приклад, щоб засвітити, чому насправді нічого не слід робити зі "значенням, яке було передано як аргумент" вихід-вихід " .
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 поводиться як копіювання в копіювання (а не як посилання). Підсумовуємо, повторюючи наступне твердження з мовних посилань на документи:
Не залежать від поведінкових відмінностей між копіюванням та викликом за посиланням.
Do not access the value that was passed as an in-out argument
висловлювання. Чому це, чи можете ви навести приклад? З того, що я розумію, аргумент, незалежно від того, чи немає, спочатку використовується для доступу всередині функції, інакше аргумент нам взагалі не потрібен.
Параметри функції за замовчуванням є константами. Спроба змінити значення параметра функції всередині тіла цієї функції призводить до помилки під час компіляції. Це означає, що ви не можете помилково змінити значення параметра. Якщо ви хочете, щоб функція модифікувала значення параметра, і ви хочете, щоб ці зміни зберігались і після закінчення виклику функції, визначте цей параметр як параметр виходу.
Якщо ви працюєте з класами, тоді, як ви сказали, ви можете змінити клас, оскільки параметр є посиланням на клас. Але це не спрацює, коли ваш параметр є типом значення ( 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
}
В основному це корисно, коли ви хочете грати з адресами змінних, що дуже корисно в алгоритмах структури даних
при використанні параметра 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)")
}
}
inout param: змінює значення передачі та локальних змінних.
func doubleInPlace(number: inout Int) {
number *= 2
print(number)
}
var myNum = 10 doubleInPlace(number: &myNum)