引言
继续学习Swift文档,从上一章节:初始化,我们学习了Swift初始化相关的内容,如类、结构体和枚举初始化方法的定义和使用、 convenience 关键词定义便捷初始化方法、初始化失败语法 init? 和强制解包 init!、 required关键词修饰init方法、用闭包和函数设置属性默认值等这些内容。现在,我们学习Swift的析构函数相关的内容。由于篇幅较长,这里分篇来记录,接下来,Fighting!
题外话:Deinitialization这个翻译过来是“反初始化”,感觉不是很对,特意去搜索一下相关内容,最后觉得“析构函数”会更适合一点,所以这里就用这个翻译结果为准吧。如果有误,欢迎指正哈.!
如果你已经掌握了这个,跳转下一章节:可选链接
析构函数
在取消释放类实例之前,将调用析构函数。 您使用deinit关键字编写析构函数,类似于使用init关键字编写初始化方法的方式。 析构函数仅在类类型上可用。
1 析构函数是如何工作的?
当不再需要实例时,Swift会自动释放其实例,以释放资源。 Swift通过自动引用计数(ARC)处理实例的内存管理,如自动引用计数中所述。 通常,在释放实例后,您无需执行手动清理。 但是,当您使用自己的资源时,可能需要自己执行一些额外的清理。 例如,如果创建一个自定义类来打开文件并向其中写入一些数据,则可能需要在释放该类实例之前关闭该文件。
每个类定义最多可以有一个析构函数。 析构函数不接受任何参数,并且写成无括号:
deinit {
// perform the deinitialization
}
在实例解除分配发生之前,会自动调用析构函数。 您不允许自己调用析构函数。 父类析构函数由其子类继承,并且父类析构函数在子类析构函数实现的末尾自动调用。 即使子类没有提供自己的析构函数,也总是会调用父类析构函数。
由于实例在调用其取消初始化之后才被释放,因此析构函数可以访问其被调用实例的所有属性,并可以基于这些属性修改其行为(例如查找需要关闭的文件名) )。
析构函数的实现
这是一个实施析构函数的示例。 本示例为一个简单的游戏定义了两种新类型,即Bank和Player。 银行类管理一种虚构货币,流通中的硬币永远不能超过10,000个。 游戏中只能有一个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
}
}
银行使用coinsInBank属性跟踪当前持有的硬币数量。 它还提供了两种方法(distribute(coins:) 和receive(coins:))来处理硬币的分发和收集。
distribute(coins:) 方法在分发之前检查银行中是否有足够的硬币。 如果没有足够的硬币,则Bank返回的数字小于所请求的数字(如果银行中没有剩余硬币,则返回零)。它返回一个整数值以指示所提供的硬币的实际数量。
receive(coins:)方法只是将接收到的硬币数量加回到银行的硬币商店中。Player类描述游戏中的玩家。
每个玩家随时都可以在钱包中存放一定数量的硬币。 这由玩家的coinInPurse属性表示:
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类还实现了一个析构函数,该初始化器在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实例存储在一个名为playerOne的可选Player变量中。 这里使用一个可选变量,因为玩家可以随时离开游戏。 可选功能可让您跟踪游戏中当前是否有玩家。
因为playerOne是可选的,所以当访问其instancesInPurse属性以打印其默认代币数量,并且每当调用其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"
在这里,玩家赢得了2,000个硬币。 现在,玩家的钱包包含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实例,因此将其释放以释放其内存。 在此之前,它的析构函数会自动被调用,并且其硬币会退还至银行。
上一章节:初始化
下一章节:可选链接
参考文档:Swift - Deinitialization