[Swift] 공식문서 15 - Deinitialization (초기화 해제)
Deinitialization — The Swift Programming Language (Swift 5.5)
Deinitialization A deinitializer is called immediately before a class instance is deallocated. You write deinitializers with the deinit keyword, similar to how initializers are written with the init keyword. Deinitializers are only available on class types
docs.swift.org
1차 수정 (2022 / 02 / 27) : 의역, 오타 및 스타일 수정
Deinitialization
디이니셜라이져(deinitializer) 는 클래스 인스턴스가 할당 해제(deallocated) 되기 바로 전에 호출됩니다. deinit 키워드를 사용해 디이니셜라이져를 작성합니다. 디이니셜라이져는 오직 클래스 타입에서만 가능합니다.
How Deinitialization Works(초기화 해제 작동 원리)
Swift는 인스턴스가 더 이상 필요하지 않을 때 리소스를 해제하기 위해 자동적으로 할당을 해제합니다. Swift 는 automatic reference counting (ARC) 를 사용해 인스턴스의 메모리를 관리합니다. 일반적으로 인스턴스가 할당 해제될 때 마다, 수동으로 정리할 필요가 없습니다. 그러나 자체 리소스로 작업시, 몇가지 추가적인 정리를 직접 수행해야 합니다. 예를 들어 파일을 열고 쓰기 위한 사용자 정의 클래스를 생성한다면, 인스턴스가 할당 해제되기 전에 파일을 닫아주어야 합니다.
클래스 정의에서는 최대 1개의 디이니셜라이져만 가질 수 있습니다. 디이니셜라이져는 파라미터를 가지지 않고, 괄호 () 없이 작성됩니다.
deinit {
// perform the deinitialization
}
디이니셜라이져는 인스턴스 할당해제가 일어나기 바로 전에 자동적으로 호출됩니다. 사용자 임의대로 디이니셜라이져를 호출하는 것은 허용되지 않습니다. 슈퍼클래스 디이니셜라이져는 서브클래스에 의해 상속받습니다. 서브클래시 디이니셜라이져 실행이 끝나면 슈퍼클래스 디이니셜라이져는 자동적으로 호출됩니다. 서브클래스가 디이니셜라이져를 제공하지 않아도, 슈퍼클래스 디이니셜라이져는 항상 호출됩니다.
디이니셜라이져가 호출될 때까지 인스턴스가 할당 해제되지 않기 때문에, 디이니셜라이져는 인스턴스의 모든 프로퍼티에 접근할 수 있으며 프토퍼티에 기반해 인스턴스의 동작을 수정할 수 있습니다.
Deinitializers in Action(실행 중인 디이니셜라이져)
여기 실행중인 디이니셜라이져 예제가 있습니다. Bank와 Player라는 두 가지 클래스를 정의합니다.
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 프로퍼티에 현재 코인 갯수를 저장합니다. coin에 대한 두 개의 메소드 distribute(coins:) & receive(coins:) 도 가집니다.
distribute(coins:) 메소드는 코인을 나눠 주기 전에 은행에 충분한 코인이 있는지 확인합니다. 코인이 충분하지 않다면 Bank는 요청된 숫자보다 적은 숫자를 리턴합니다. 제공받은 코인의 실제 갯수를 나타내는 정수 값을 리턴합니다.
receive(coins:) 메소드는 간단히 coinsInBank 에 받은 코인의 갯수를 더해주면됩니다.
Player 클래스에서 각 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 인스턴스는 초기화 중에 Bank 로부터 특정 코인 갯수를 받으며 초기화됩니다. 코인이 충분하지 않으면 Player 인스턴스는 해당 갯수보다 적게 받을 수 있습니다.
Player 클래스는 Bank에서 일정 갯수의 코인을 회수해 player의 지갑에 추가하는 win(coins:) 메소드를 가집니다. Player 클래스는 Player 인스턴스가 할당해제 되기 전에 디이니셜라이져도 실행합니다. 디이니셜라이져가 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에 저장됩니다. player가 언제든 게임에서 떠날 수 있도록 옵셔널 변수로 선언했습니다.
playerOne이 옵셔널이므로 win(coins:) 메소드 사용을 위해 !를 사용합니다. (coinsInPurse 프로퍼티에 접근시에도 ! 사용)
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"
player가 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"
player가 게임을 떠납니다. playerOne 을 nil로 설정함으로써 이를 나타냅니다.
이 지점에서 playerOne 변수의 Player에 대한 참조(reference)는 사라집니다. 다른 어떤 프로퍼티나 변수도 Player 인스턴스를 참조하지 않습니다. 이제 메모리를 비우기 위해 할당해제가 됩니다.
이 일이 일어나기 직전에 디이니셜라니져가 자동으로 호출되고 코인들은 은행으로 반환됩니다. (은행의 코인이 다시 10000개가 됩니다.)
Swift는 인스턴스가 할당 해제 될 때 자동적으로 디이니셜라이져를 호출해 메모리를 비워준다.