Статьи

Swift From Scratch: введение в функции

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

В этой статье мы вначале сосредоточимся на основах. Затем мы продолжим исследовать более сложный синтаксис и варианты использования в следующей статье. Важно, чтобы вы не просматривали основы, так как они необходимы для понимания того, откуда исходит сила функции. Давайте начнем с анализа анатомии функции в Swift на примере.

Функция – это не что иное, как блок кода, который может быть выполнен всякий раз, когда это необходимо. Давайте рассмотрим пример базовой анатомии функции Swift. Запустите Xcode и создайте новую игровую площадку. Добавьте следующее определение функции на игровую площадку.

1
2
3
func printHelloWorld() {
    print(“Hello World!”)
}

Функция начинается с ключевого слова func , за которым следует имя функции printHelloWorld в нашем примере. Как и во многих других языках, за именем функции следует пара круглых скобок, которые содержат параметры функции – входные данные для функции.

Тело функции заключено в пару фигурных скобок. Функция printHelloWorld() содержит один оператор, который печатает строку Hello World! на стандартный вывод. Вот как выглядит основная функция в Swift. Синтаксис простой, чистый и минималистский.

Вы можете вызвать функцию, введя имя функции, а затем пару скобок.

1
printHelloWorld()

Давайте сделаем приведенный выше пример более сложным, добавив параметры в определение функции. Это просто означает, что мы предоставляем функции входные значения, которые она может использовать в теле функции. В следующем примере мы определяем printMessage(message:) , которая принимает один параметр message типа String .

1
2
3
func printMessage(message: String) {
    print(message)
}

Функция может принимать несколько параметров или входных значений. Параметры заключены в круглые скобки, которые следуют за именем функции. За именем параметра следует двоеточие и тип параметра. Как вы помните, это очень похоже на объявление переменной или константы. Это просто говорит о том, что параметр message имеет тип String .

Вместо того, чтобы печатать жестко закодированную строку, как мы это делали в функции printHelloWorld() , мы печатаем значение параметра message . Это делает функцию гибкой и более полезной.

Вызов функции очень похож на то, что мы видели ранее. Разница лишь в том, что мы передаем аргумент при вызове функции.

1
printMessage(message: “Hello World!”)

Обратите внимание, что термины параметры и аргументы часто используются взаимозаменяемо, но в Swift есть небольшая семантическая разница. В Swift параметры – это значения, указанные в определении функции, а аргументы – это значения, передаваемые функции при ее вызове.

Как я упоминал ранее, синтаксис функций очень гибкий, и вас не должно удивлять то, что вполне возможно передать несколько аргументов функции. В следующем примере мы создаем вариант функции printMessage(message:times:) который позволяет печатать сообщение несколько раз.

1
2
3
4
5
func printMessage(message: String, times: Int) {
    for i in 0..<times {
        print(“\(i) \(message)”)
    }
}

Хотя имя функции идентично имени исходной функции printMessage(message:) , тип функции отличается.

Важно, чтобы вы поняли предыдущее предложение. Прочитайте это снова.

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

Тип первой функции (String) -> () , а тип второй функции (String, Int) -> () . Название обеих функций одинаково. Не беспокойтесь о символе -> . Его значение станет ясным через несколько минут, когда мы обсудим типы возвращаемых данных.

Вторая printMessage(message:times:) определяет два параметра: message типа String и times типа Int . Это определение иллюстрирует одну из особенностей, которые Swift использует в Objective-C, – читаемые имена функций и методов. Хотя имя функции – printMessage , легко понять, что должна делать функция, прочитав имена параметров функции.

Во второй функции printMessage(message:times:) мы создаем цикл forin чтобы печатать строку message раз. Мы используем оператор полуоткрытого диапазона, ..< , как мы видели ранее в этой серии.

Вызов функции

Когда мы начинаем печатать printMessage на игровой площадке, Xcode отображает обе функции в меню автозаполнения. Благодаря типу функции легко выбрать интересующую нас функцию. Вызов второй функции printMessage(message:times:) так же прост:

1
printMessage(message: “Hello World”, times: 3)

Одна из моих любимых функций – возможность определять значения по умолчанию для параметров. Это может звучать глупо, если вы работаете с языком, в котором эта функция использовалась целую вечность, но это очень здорово, если вы работали с C и Objective-C много лет.

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

1
import UIKit

Давайте сначала определим printDate(date:format:) без использования значений по умолчанию для любого из параметров.

1
2
3
4
5
func printDate(date: Date, format: String) {
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = format
    print(dateFormatter.string(from: date))
}

Если вы не знакомы с платформой Foundation и не понимаете, что происходит в теле функции, тогда это нормально. В центре внимания этого примера не реализация форматирования даты. В printDate(date:format:) мы используем значение параметра format для форматирования значения date . Если мы не передадим значение параметра format , компилятор выдаст ошибку.

Аргумент функции отсутствует

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

1
2
3
4
5
func printDate(date: Date, format: String = “YY/MM/dd”) {
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = format
    print(dateFormatter.string(from: date))
}

Определение значения по умолчанию так же просто, как указание значения в списке параметров в определении функции. В результате компилятор больше не жалуется и ошибка исчезает.

1
printDate(date: Date())

Даже если мы указали значение по умолчанию для параметра format , мы все равно можем передать значение, если захотим.

1
printDate(date: Date(), format: “dd/MM/YY”)

Обратите внимание, что Apple рекомендует позиционировать параметры со значением по умолчанию в конце списка параметров. Это, безусловно, хорошая идея, распространенная в большинстве других языков программирования, которые поддерживают необязательные параметры.

Функции, которые мы видели до сих пор, ничего не возвращают нам, когда мы их вызываем. Давайте сделаем printDate(date:format:) более полезной, возвращая отформатированную дату в виде строки вместо печати отформатированной даты в теле функции. Это требует двух изменений, как вы можете видеть ниже.

1
2
3
4
5
func printDate(date: Date, format: String = “YY/MM/dd”) -> String {
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = format
    return dateFormatter.string(from: date)
}

Первое, что мы изменим, – это определение функции. После списка параметров мы указываем тип возвращаемого значения String . Типу возврата предшествует символ -> . Если вы работали с CoffeeScript, то это будет выглядеть знакомо.

Вместо того, чтобы печатать отформатированную дату с помощью функции print(_:separator:terminator:) , мы используем ключевое слово return для возврата значения из функции. Это все, что нам нужно сделать. Давайте попробуем это.

1
2
3
let formattedDate = printDate(date: Date(), format: “dd/MM/YY”)
 
print(formattedDate)

Мы вызываем printDate(date:format:) , сохраняем возвращаемое значение в константе formattedDate и печатаем значение formattedDate в стандартном выводе. Обратите внимание, что имя функции printDate(date:format:) больше не отражает то, что она делает, поэтому вы можете вместо этого изменить ее на formatDate .

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

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

Компилятор показывает предупреждение

Это интересно. У Swift нет проблем с тем, что мы сохраняем возвращаемое значение функции printHelloWorld() в константе, но он предупреждает нас, что тип возвращаемого значения не тот, который мы могли бы себе представить.

Что тут происходит? Каждая функция в Swift возвращает значение, даже если мы не определяем тип возвращаемого значения в определении функции. Когда функция явно не указывает тип возвращаемого значения, функция неявно возвращает Void , что эквивалентно пустому кортежу или () для краткости. Вы можете увидеть это на панели вывода детской площадки, и это также упоминается в предупреждении о выходе компилятора.

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

1
let value: () = printHelloWorld()

Еще одна замечательная особенность Swift – возможность возвращать несколько значений из функции, возвращая кортеж. Следующий пример иллюстрирует, как это работает. Позвольте мне повторить, что не важно, что вы понимаете, как timeComponentsForDate(date:) выполняет свою работу. Фокусом является возвращаемое значение функции, кортеж с тремя элементами.

1
2
3
4
5
6
7
func timeComponentsForDate(_ date: Date) -> (hour: Int, minute: Int, second: Int) {
    let dateComponents = Calendar.current.dateComponents([.hour, .minute, .second], from: date)
    let hour = dateComponents.hour
    let minute = dateComponents.minute
    let second = dateComponents.second
    return (hour ?? 0, minute ?? 0, second ?? 0)
}

Функция принимает один аргумент, экземпляр Date , и возвращает кортеж с тремя помеченными значениями. Маркировка значений кортежа только для удобства; возможно опустить метки.

1
2
3
4
5
6
7
func timeComponentsForDate(_ date: Date) -> (Int, Int, Int) {
    let dateComponents = Calendar.current.dateComponents([.hour, .minute, .second], from: date)
    let hour = dateComponents.hour
    let minute = dateComponents.minute
    let second = dateComponents.second
    return (hour ?? 0, minute ?? 0, second ?? 0)
}

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

1
2
3
4
5
let timeComponents = timeComponentsForDate(Date())
 
print(timeComponents.hour)
print(timeComponents.minute)
print(timeComponents.second)

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
func timeComponentsForDate(_ date: Date) -> (hour: Int, minute: Int, second: Int)?
    let dateComponents = Calendar.current.dateComponents([.hour, .minute, .second], from: date)
 
    guard let hour = dateComponents.hour else {
        return nil
    }
 
    guard let minute = dateComponents.minute else {
        return nil
    }
 
    guard let second = dateComponents.second else {
        return nil
    }
 
    return (hour, minute, second)
}

В этом уроке мы изучили основы функций в Swift. Важно понимать синтаксис функций, потому что в следующей статье мы рассмотрим более сложные функции, основанные на том, что мы рассмотрели в этом руководстве.

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

Если вы хотите узнать, как использовать Swift 3 для кодирования реальных приложений, ознакомьтесь с нашим курсом « Создание приложений для iOS с помощью Swift 3» . Если вы новичок в разработке приложений для iOS или хотите перейти с Objective-C, этот курс поможет вам начать работу с Swift для разработки приложений.

  • стриж
    Создание приложений для iOS с Swift 3
    Маркус Мюльбергер
  • стриж
    Запрограммируйте игру с боковой прокруткой с помощью Swift 3 и SpriteKit
    Дерек Дженсен
  • IOS
    Что нового в iOS 10
    Маркус Мюльбергер