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