Основы Swift / 13.4. Деинициализация


Видео


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

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

Определения класса могут иметь не более одного деинициализатора на класс. Деинициализтор не имеет никаких параметров и записывается без круглых скобок:
@{13.4\1}
deinit {
    // выполнить деинициализацию
}
Автоматический вызов деинициализаторов
Деинициализаторы вызываются автоматически прямо перед деаллокацией объекта. Вы не можете вызвать деинициализатор самостоятельно. Деинициализаторы надкласса наследуются их подклассами, а деинициализатор надкласса вызывается автоматически в конце реализации деинциализатора подкласса. Деинициализаторы надкласса всегда вызываются, даже если подкласс не предоставляет своего собственного деинициализатора.

Так как объект не деаллоциируется до тех пор, пока не будет вызван его деинициализатор, то деинициализатор может получить доступ ко всем свойствам объекта, для которого он вызван, и может менять своё поведение в зависимости от этих свойств (как например поиск имени файла, который следует закрыть).
Деинициализаторы в действии
Здесь приведён пример деинициализатора в действии. Этот пример определяет два новых типа, Bank и Player, для простой игры. Класс Bank управляет валютой, которой не может быть более 10,000 в обороте. Так как может быть только один банк в игре, то класс Bank реализован в качестве класса со свойствами типа и методами для хранения и управления его текущим состоянием:
@{13.4\2\1}
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:) просто добавляет полученное число монет назад в банковское хранилище.
@{13.4\2\2}
Класс 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 может получить и меньшее количество, если монет недостаточно.
@{13.4\2\3}
Класс Player определяет метод win(coins:), который получает определённое количество монет из банка и прибавляет их к счёту игрока. Класс Player также реализует деструктор, который вызывается прежде, чем объект Player будет деаллоцирован. Здесь деструктор просто возвращает все монеты игрока в банк:
var playerOne: Player? = Player(coins: 100)
print("A new player has joined the game with \(playerOne!.coinsInPurse) coins")
// Выведет "A new player has joined the game with 100 coins"
print("There are now \(Bank.coinsInBank) coins left in the bank")
// Выведет "There are now 9900 coins left in the bank"
Новый экземпляр типа Player создаётся с запросом на 100 монет, если они доступны. Объект Player хранится в опциональной переменной Player с названием playerOne. Опциональная переменная используется ввиду того, что игроки могут покинуть игру в любой момент. Опционал позволяет Вам отслеживать, находится ли текущий игрок в игре.
@{13.4\2\4}
Так как playerOne - это опционал, то он квалифицируется восклицательным знаком (!), когда производится доступ к его свойству coinsInPurse для вывода его дефолтного значения монет или когда вызывается метод win(coins:):
playerOne!.win(coins: 2_000)
print("PlayerOne won 2000 coins & now has \(playerOne!.coinsInPurse) coins")
// Выводит "PlayerOne won 2000 coins & now has 2100 coins"
print("The bank now only has \(Bank.coinsInBank) coins left")
// Выводит "The bank now only has 7900 coins left"
@{13.4\2\5}
В этом примере игрок выигрывает 2000 монет. Счёт игрока теперь содержит 2100 монет, а в банке осталось только 7900 монет.
playerOne = nil
print("PlayerOne has left the game")
// Выведет "PlayerOne has left the game"
print("The bank now has \(Bank.coinsInBank) coins")
// Выведет "The bank now has 10000 coins"
Игрок только что покинул игру. Это определяется присвоением nil переменной playerOne, что означает "нет объекта Player". В момент, когда это происходит, ссылка playerOne на переменную Player разбивается. Никакие другие свойства или переменные не ссылаются на этот объект, так что он деаллоциируется для высвобождения памяти. Прежде, чем это случится, автоматически будет вызван деинициализатор, а его монеты вернутся в банк.