Spec-Zone.ru › Swift Language

Методы

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

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

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

Методы экземпляров

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

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

Вот пример, который определяет простой Counter класс, который может использоваться для подсчета количества раз, когда происходит действие:

class Counter {
    var count = 0
    func increment() {
        count += 1
    }
    func increment(by amount: Int) {
        count += amount
    }
    func reset() {
        count = 0
    }
}

Класс Counter определяет три метода экземпляра:

  • increment() увеличивает счетчик на 1.
  • increment(by: Int) увеличивает счетчик на заданное целое число.
  • reset() сбрасывает счетчик в ноль.

Класс Counter также объявляет переменное свойство count для отслеживания текущего значения счетчика.

Вызываете методы экземпляров с тем же синтаксисом точек, что и свойства:

let counter = Counter()
// the initial counter value is 0
counter.increment()
// the counter's value is now 1
counter.increment(by: 5)
// the counter's value is now 6
counter.reset()
// the counter's value is now 0

Параметры функций могут иметь и имя (для использования в теле функции) и метку аргумента (для использования при вызове функции), как описано в <doc:Functions#Function-Argument-Labels-and-Parameter-Names>. То же самое верно для параметров методов, поскольку методы — это просто функции, связанные с типом.

Свойство self

Каждый экземпляр типа имеет неявное свойство, называемое self, которое точно эквивалентно самому экземпляру. Вы используете свойство self для ссылки на текущий экземпляр внутри собственных методов экземпляра.

Метод increment() в примере выше можно было бы написать так:

func increment() {
    self.count += 1
}

На практике вам не нужно часто писать self в своем коде. Если вы явно не напишете self, Swift предполагает, что вы ссылаетесь на свойство или метод текущего экземпляра всякий раз, когда используете известное имя свойства или метода внутри метода. Это предположение продемонстрировано использованием count (а не self.count) внутри трех методов экземпляров для Counter.

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

Здесь self устраняет неоднозначность между параметром метода, называемым x, и свойством экземпляра, также называемым x:

struct Point {
    var x = 0.0, y = 0.0
    func isToTheRightOf(x: Double) -> Bool {
        return self.x > x
    }
}
let somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOf(x: 1.0) {
    print("This point is to the right of the line where x == 1.0")
}
// Prints "This point is to the right of the line where x == 1.0"

Без префикса self Swift предположит, что оба использования x относятся к параметру метода, называемому x.

Изменение типов значений внутри методов экземпляров

Структуры и перечисления являются типами значений. По умолчанию свойства типа значений не могут быть изменены внутри его методов экземпляра.

Однако, если вам нужно изменить свойства вашей структуры или перечисления внутри определенного метода, вы можете включить мутирующее поведение для этого метода. Метод может затем мутировать (то есть изменить) свои свойства внутри метода, и любые изменения, которые он вносит, записываются обратно в исходную структуру по окончании метода. Метод также может присвоить совершенно новый экземпляр своему неявной свойству self, и этот новый экземпляр заменит существующий по завершении метода.

Вы можете включить это поведение, поместив ключевое слово mutating перед ключевым словом func для этого метода:

struct Point {
    var x = 0.0, y = 0.0
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        x += deltaX
        y += deltaY
    }
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
print("The point is now at (\(somePoint.x), \(somePoint.y))")
// Prints "The point is now at (3.0, 4.0)"

Структура Point выше определяет мутирующий метод moveBy(x:y:), который перемещает экземпляр Point на определенное количество. Вместо возвращения нового объекта, этот метод фактически изменяет точку, к которой он применяется. Ключевое слово mutating добавляется к его определению, чтобы разрешить ему изменять свои свойства.

Обратите внимание, что вы не можете вызвать мутирующий метод для константы типа структуры, потому что ее свойства не могут быть изменены, даже если они являются переменными свойствами, как описано в <doc:Properties#Stored-Properties-of-Constant-Structure-Instances>:

let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveBy(x: 2.0, y: 3.0)
// this will report an error

Присваивание self внутри мутирующего метода

Мутирующие методы могут присвоить совершенно новый экземпляр свойству self. Пример Point, показанный выше, можно было бы написать следующим образом вместо этого:

struct Point {
    var x = 0.0, y = 0.0
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        self = Point(x: x + deltaX, y: y + deltaY)
    }
}

Эта версия мутирующего метода moveBy(x:y:) создает новую структуру, значения свойств x и y которой установлены в целевое местоположение. Конечный результат вызова этой альтернативной версии метода будет точно таким же, как и при вызове предыдущей версии.

Мутирующие методы для перечислений могут установить неявный параметр self на другое состояние того же перечисления:

enum TriStateSwitch {
    case off, low, high
    mutating func next() {
        switch self {
        case .off:
            self = .low
        case .low:
            self = .high
        case .high:
            self = .off
        }
    }
}
var ovenLight = TriStateSwitch.low
ovenLight.next()
// ovenLight is now equal to .high
ovenLight.next()
// ovenLight is now equal to .off

Этот пример определяет перечисление для трехпозиционного переключателя. Переключатель циклически переходит между тремя разными состояниями питания (off, low и high) каждый раз, когда вызывается его метод next().

Методы типа

Методы экземпляров, как описано выше, — это методы, которые вы вызываете для экземпляра определенного типа. Вы также можете определить методы, которые вызываются для самого типа. Эти методы называются методами типа. Вы указываете методы типа, написав ключевое слово static перед ключевым словом func метода. Классы могут использовать ключевое слово class вместо этого, чтобы разрешить подклассам переопределять реализацию метода суперкласса этого метода.

Примечание: в Objective-C вы можете определить методы уровня типа только для Objective-C классов. В Swift вы можете определить методы уровня типа для всех классов, структур и перечислений. Каждый метод типа явно ограничен типом, который он поддерживает.

Методы типа вызываются с синтаксисом точек, как методы экземпляров. Однако вызываете методы типа для типа, а не для экземпляра этого типа. Вот как вызвать метод типа для класса, называемого SomeClass:

class SomeClass {
    class func someTypeMethod() {
        // type method implementation goes here
    }
}
SomeClass.someTypeMethod()

В теле метода типа неявное свойство self относится к самому типу, а не к экземпляру этого типа. Это означает, что вы можете использовать self для устранения неоднозначности между свойствами типа и параметрами метода типа, как и для свойств экземпляра и параметров метода экземпляра.

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

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

Все уровни игры (кроме первого) заблокированы при первом запуске игры. Каждый раз, когда игрок завершает уровень, этот уровень разблокируется для всех игроков на устройстве. Структура LevelTracker использует свойства и методы типа для отслеживания, какие уровни игры были разблокированы. Она также отслеживает текущий уровень для отдельного игрока.

struct LevelTracker {
    static var highestUnlockedLevel = 1
    var currentLevel = 1

    static func unlock(_ level: Int) {
        if level > highestUnlockedLevel { highestUnlockedLevel = level }
    }

    static func isUnlocked(_ level: Int) -> Bool {
        return level <= highestUnlockedLevel
    }

    @discardableResult
    mutating func advance(to level: Int) -> Bool {
        if LevelTracker.isUnlocked(level) {
            currentLevel = level
            return true
        } else {
            return false
        }
    }
}

Структура LevelTracker отслеживает максимальный уровень, который любой игрок разблокировал. Это значение хранится в свойстве типа, называемом highestUnlockedLevel.

LevelTracker также определяет две функции типа для работы со свойством highestUnlockedLevel. Первая — это функция типа, называемая unlock(_:), которая обновляет значение highestUnlockedLevel всякий раз, когда разблокируется новый уровень. Вторая — это функция удобства типа, называемая isUnlocked(_:), которая возвращает true, если определенный номер уровня уже разблокирован. (Обратите внимание, что эти методы типа могут получить доступ к свойству типа highestUnlockedLevel без необходимости написания его как LevelTracker.highestUnlockedLevel.)

В дополнение к своему свойству типа и методам типа, LevelTracker отслеживает прогресс отдельного игрока в игре. Она использует свойство экземпляра, называемое currentLevel, для отслеживания уровня, на котором играет игрок.

Для помощи в управлении свойством currentLevel, LevelTracker определяет метод экземпляра, называемый advance(to:). Перед обновлением currentLevel этот метод проверяет, уже ли разблокирован запрашиваемый новый уровень. Метод advance(to:) возвращает значение Boolean, чтобы указать, удалось ли ему установить currentLevel. Поскольку для кода, который вызывает метод advance(to:), не обязательно является ошибкой игнорирование возвращаемого значения, эта функция помечена атрибутом @discardableResult. Для получения дополнительной информации об этом атрибуте см. Атрибуты.

Структура LevelTracker используется с классом Player, показанным ниже, для отслеживания и обновления прогресса отдельного игрока:

class Player {
    var tracker = LevelTracker()
    let playerName: String
    func complete(level: Int) {
        LevelTracker.unlock(level + 1)
        tracker.advance(to: level + 1)
    }
    init(name: String) {
        playerName = name
    }
}

Класс Player создаёт новый экземпляр LevelTracker для отслеживания прогресса игрока. Он также предоставляет метод под названием complete(level:), который вызывается всякий раз, когда игрок завершает определённый уровень. Этот метод разблокирует следующий уровень для всех игроков и обновляет прогресс игрока, чтобы перевести его на следующий уровень. (Булево возвращаемое значение advance(to:) игнорируется, поскольку известно, что уровень был разблокирован вызовом LevelTracker.unlock(_:) на предыдущей строке.)

Вы можете создать экземпляр класса Player для нового игрока и посмотреть, что произойдёт, когда игрок завершит первый уровень:

var player = Player(name: "Argyrios")
player.complete(level: 1)
print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
// Prints "highest unlocked level is now 2"

Если вы создадите второго игрока, которого вы попытаетесь переместить на уровень, который ещё не разблокирован ни одним игроком в игре, попытка установить текущий уровень игрока завершится неудачей:

player = Player(name: "Beto")
if player.tracker.advance(to: 6) {
    print("player is now on level 6")
} else {
    print("level 6 hasn't yet been unlocked")
}
// Prints "level 6 hasn't yet been unlocked"

This source file is part of the Swift.org open source project
Copyright © 2014 - 2025 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API