Deinitializer вызывается непосредственно перед экземпляром класса освобождаться. Вы пишете деинициализаторы с deinitключевым словом, подобно тому, как инициализаторы пишутся с initключевым словом. Деинициализаторы доступны только для типов классов.

Как работает деинициализация

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

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

deinit {

// perform the deinitialization

}

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

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

Деинициализаторы в действии

Вот пример деинициализатора в действии. Этот пример определяет два новых типа Bankи Playerдля простой игры. BankКласс управляет выдуманной валютой, которая никогда не может иметь более 10000 монет в обращении. В Bankигре может быть только один , поэтому Bankон реализован в виде класса со свойствами типа и методами для хранения и управления его текущим состоянием:

class Bank {

static var coinsInBank = 10_000

static func distribute(coins numberOfCoinsRequested: Int) -> Int {

let numberOfCoinsToVend = min(numberOfCoinsRequested, coinsInBank)

coinsInBank -= numberOfCoinsToVend

return numberOfCoinsToVend

}

static func receive(coins: Int) {

coinsInBank += coins

}

}

Bankотслеживает текущее количество монет, которые оно хранит со своим coinsInBankсвойством. Он также предлагает два метода - distribute(coins:)и receive(coins:)- для распределения и сбора монет.

distribute(coins:)Метод проверяет , что имеется достаточное количество монет в банке до их распространения. Если монет недостаточно, Bankвозвращает меньшее число, чем запрошенное число (и возвращает ноль, если в банке не осталось монет). Он возвращает целочисленное значение, чтобы указать фактическое количество монет, которые были предоставлены.

receive(coins:)Метод просто добавляет полученное количество монет обратно в магазин монет банки.

PlayerКласс описывает игрок в игре. У каждого игрока есть определенное количество монет, хранящихся в его кошельке в любое время. Это представлено coinsInPurseсвойством игрока :

class Player {

var coinsInPurse: Int

init(coins: Int) {

coinsInPurse = Bank.distribute(coins: coins)

}

func win(coins: Int) {

coinsInPurse += Bank.distribute(coins: coins)

}

deinit {

Bank.receive(coins: coinsInPurse)

}

}

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

PlayerКласс определяет win(coins:)метод, который извлекает определенное количество монет из банка и добавляет их на кошелек игрока. PlayerКласс также реализует deinitializer, который вызывается непосредственно перед Playerэкземпляр освобождаться. Здесь деинициализатор просто возвращает все монеты игрока в банк:

var playerOne: Player? = Player(coins: 100)

print("A new player has joined the game with \(playerOne!.coinsInPurse) coins")

// Prints "A new player has joined the game with 100 coins"

print("There are now \(Bank.coinsInBank) coins left in the bank")

// Prints "There are now 9900 coins left in the bank"

Создается новый Playerэкземпляр с запросом на 100 монет, если они доступны. Этот Playerэкземпляр хранится в необязательной Playerпеременной с именем playerOne. Здесь используется необязательная переменная, потому что игроки могут покинуть игру в любой момент. Опционально позволяет отслеживать, есть ли в данный момент игрок в игре.

Поскольку playerOneэто необязательно, он квалифицируется восклицательным знаком ( !), когда к его coinsInPurseсвойству обращаются, чтобы напечатать количество монет по умолчанию и всякий раз, когда win(coins:)вызывается его метод:

playerOne!.win(coins: 2_000)

print("PlayerOne won 2000 coins & now has \(playerOne!.coinsInPurse) coins")

// Prints "PlayerOne won 2000 coins & now has 2100 coins"

print("The bank now only has \(Bank.coinsInBank) coins left")

// Prints "The bank now only has 7900 coins left"

Здесь игрок выиграл 2000 монет. Теперь кошелек игрока содержит 2100 монет, а в банке осталось всего 7900 монет.

playerOne = nil

print("PlayerOne has left the game")

// Prints "PlayerOne has left the game"

print("The bank now has \(Bank.coinsInBank) coins")

// Prints "The bank now has 10000 coins"

Игрок покинул игру. На это указывает установка необязательной playerOneпеременной nil, что означает «нет Playerэкземпляра». В тот момент, когда это происходит, playerOneссылка переменной на Playerэкземпляр прерывается. Никакие другие свойства или переменные по-прежнему не ссылаются на Playerэкземпляр, поэтому он освобождается для освобождения памяти. Непосредственно перед тем, как это произойдет, его деинициализатор вызывается автоматически, а его монеты возвращаются в банк.