Перейти к основному содержимому

Параметры функций и читаемость API в Swift

Разработчику Архитектору

Идея «вызов как фраза»

Напоминание: общие понятия функций в программе — функции в коде.

В Swift имя функции при вызове часто складывается из меток параметров. Это не декорация: метки входят в идентификатор метода и делают код похожим на осмысленное предложение.

func move(from start: Point, to end: Point) { /* ... */ }
move(from: origin, to: destination)

Сравните с вызовом без меток: move(origin, destination) — короче, но при шести параметрах читаемость падает. Apple API (UIKit, SwiftUI, Foundation) последовательно используют метки — привыкание к ним облегчает чтение документации и чужого кода.


Внешнее и внутреннее имя

У каждого параметра (кроме особых случаев) могут быть:

  • внешнее имя — видно при вызове;
  • внутреннее имя — используется в теле функции.
func greet(person name: String, times count: Int) {
for _ in 0..<count {
print("Hello, \(name)")
}
}
greet(person: "Мария", times: 3)

Здесь person и times — внешние, name и count — внутренние.

Если внешнее имя не указано, по умолчанию оно совпадает с внутренним. Первый параметр метода экземпляра (instance method) по соглашению не имеет внешней метки, если не задана явно — поэтому array.append(element) выглядит естественно.


Подчёркивание _ — скрыть метку

Когда метка при вызове только мешает:

func add(_ a: Int, _ b: Int) -> Int { a + b }
add(2, 3)

Используйте _ осознанно: для «математических» функций и оператороподобных методов. Для публичного API библиотеки чаще оставляют метки — так проще искать вызовы и понимать аргументы.


Значения по умолчанию

Параметр с default можно не передавать:

func connect(host: String, port: Int = 443, useTLS: Bool = true) { /* ... */ }
connect(host: "api.example.com")
connect(host: "legacy.example.com", port: 8080, useTLS: false)

Правила:

  • параметры с default обычно идут после обязательных;
  • при вызове пропущенные аргументы подставляются из объявления;
  • default вычисляется в момент вызова (важно для Date(), случайных значений).

Вариадические параметры

Функция может принять произвольное число однотипных аргументов через ...:

func sum(_ numbers: Int...) -> Int {
numbers.reduce(0, +)
}
sum(1, 2, 3, 4)

Внутри тела numbers — это массив ([Int]). Вариадический параметр, как правило, один на функцию и стоит последним среди обычных параметров (до inout).


Параметры inout

По умолчанию аргументы в Swift передаются по значению (для value types — копия). Чтобы функция могла изменить переменную вызывающего, используется inout:

func swapValues(_ a: inout Int, _ b: inout Int) {
let temp = a
a = b
b = temp
}
var x = 1, y = 2
swapValues(&x, &y)

При вызове обязателен префикс &. Это явный сигнал: «здесь возможен побочный эффект». Нельзя передать let или литерал — только изменяемую переменную.

inout не делает параметр ссылочным типом в смысле class: для структур по-прежнему работает copy-on-write у контейнеров, но сама переменная в области видимости вызывающего обновится.


Trailing closure — замыкание последним аргументом

Если последний параметр — замыкание, его можно вынести за скобки:

func perform(task: String, handler: () -> Void) {
print(task)
handler()
}
perform(task: "backup") {
print("done")
}

При нескольких замыканиях trailing применяют к последнему; остальные — в скобках (в Swift 5.3+ — множественные trailing closures для DSL, например SwiftUI).

Связь с управляющими конструкциями и коллекциями:

let ids = users.filter { $0.isActive }.map { $0.id }

Здесь $0 — сокращённый аргумент замыкания; полная форма: { user in user.id }.


Тип функции как значение

Сигнатура (Parameters) -> ReturnType — полноценный тип:

func apply(_ value: Int, using operation: (Int) -> Int) -> Int {
operation(value)
}
let double: (Int) -> Int = { $0 * 2 }
apply(10, using: double)

Так задают колбэки, стратегии сортировки, обработчики событий. В современном коде часть колбэков заменена на async/await (асинхронность), но типы функций остаются в стандартной библиотеке (sort, map).


Вложенные и возвращающие функции

Функция может быть объявлена внутри другой — доступ к локальным переменным внешней:

func makeCounter() -> () -> Int {
var count = 0
func increment() -> Int {
count += 1
return count
}
return increment
}
let counter = makeCounter()
counter() // 1
counter() // 2

Возврат функции из функции — основа простых фабрик и замыканий без явного closure литерала.


@escaping и захват (кратко)

Если замыкание сохраняется и вызывается после возврата функции (асинхронно, в свойстве), параметр помечают @escaping. Иначе компилятор считает, что замыкание завершится до выхода из функции — это безопаснее для памяти.

При захвате self в классах избегайте циклов: [weak self] или [unowned self] в списке захвата. Подробнее — ООП в Swift и справочник.


Соглашения для своего API

СитуацияРекомендация
Публичный метод с 3+ параметрамиОсмысленные внешние метки
«Оператор», математика_ для всех или большинства
Побочный эффект над аргументомinout + ясное имя
Асинхронный колбэк@escaping, явный [weak self] в документации
SwiftUI / DSLTrailing closure, @ViewBuilder (отдельно в фреймворках)

Связанные материалы


См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).