Методы - это функции, связанные с конкретным типом. Классы, структуры и перечисления могут определять методы экземпляра, которые инкапсулируют конкретные задачи и функциональные возможности для работы с экземпляром данного типа. Классы, структуры и перечисления могут также определять методы типа, которые связаны с самим типом. Методы типа аналогичны методам класса в 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

Параметры функции могут иметь как имя (для использования в теле функции), так и метку аргумента (для использования при вызове функции), как описано в разделе Метки аргумента функции и Имена параметров . То же самое относится и к параметрам метода, потому что методы - это просто функции, связанные с типом.

Собственная собственность

Каждый экземпляр типа имеет неявное свойство с именем 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Ключевое слово добавляется к его определению , чтобы дать ему возможность изменить его свойства.

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

let fixedPoint = Point(x: 3.0, y: 3.0)

fixedPoint.moveBy(x: 2.0, y: 3.0)

// this will report an error

Присвоение себе внутри метода мутации

Мутирующие методы могут назначать совершенно новый экземпляр неявному 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

Этот пример определяет перечисление для переключателя с тремя состояниями. Циклы переключения между тремя различными состояниями питания ( offlowи high) каждый раз его next()метод вызывается.

Методы типа

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

ЗАМЕТКА

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

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

Чтобы помочь управлять currentLevelсвойством, LevelTrackerопределяет метод экземпляра с именем advance(to:). Перед обновлением currentLevelэтот метод проверяет, разблокирован ли запрошенный новый уровень. advance(to:)Метод возвращает логическое значение, определяющее , является ли или не был фактически в состоянии установить его 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 has not yet been unlocked")

}

// Prints "level 6 has not yet been unlocked"