结构(Deinitialization)

338 阅读4分钟

原文

在实例销毁之前deinitializer立即被调用。使用deinit关键字来写deinitializers,像如何用init关键字写初始化器一样。deinitializers只在类类型中可用。

Deinitialization如何工作(How Deinitialization Works)

当实例不需要的时候swift自动释放你的实例,来释放资源。swift通过自动引用计数来处理实例的内存管理,像Automatic Reference Counting中描述的。当你的实例释放的时候一般不需要执行手动清除。不过当年你使用你自己的资源,你可能需要执行一些自己的额外的清除。例如,如果你创建了一个自定义类来打开一个文件或者给他写一些数据,可能需要在类实例释放之前关闭文件。

每个类的类定义可以有最多一个deinitializer。deinitializer不接受任何参数,不用括号写:

deinit {
    // perform the deinitialization
}

Deinitializers自动调用,在实例释放之前。不能自己调用deinitializer。父类的deinitializer被他的子类继承,父类的deinitializer在子类deinitializer实现最后自动调用。父类deinitializer通常会被调用,即使他的子类没有提供自己的deinitializer。

因为一个实例在它的deinitializer调用之前不会被释放,deinitializer可以访问调用它的实例的全部属性并且可以在这些属性基础上修改它的表现(例如查看需要关闭的文件的名字)。

Deinitialization的使用

这里是一个deinitializer在使用中的例子。这个例子定义了两个新的类型,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属性存储的硬币的数目。也提供了两个方法--distribut(coins:)和receive(coins:)--来处理硬币的发布和收集。

方法distribut(coins:)检查在发布之前银行里有足够的硬币。如果没有足够的硬币,bank返回一个比请求的数字小的数字(如果在银行中没有硬币了返回0)。他返回一个整型值来指明提供的硬币的真实值。

方法receive(coins:)简单的增加接受的硬币的数量到银行的存储硬币中。

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实例可能收到的比那个数字少。

player类定义了一个win(coins:)方法,从银行中获取确定的硬币的个数并且将他们加到玩家的钱包中。Player类也实现了一个deinitializer,在玩家实例销毁之前调用。这里,deinitializer简单的返回玩家全部的硬币给银行:

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是可选的,当访问它的coinsInPurse属性来打印它的默认的conis数时可以使用感叹号,任何它的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表示,意味着“no Player instance。”在这发生的时候,playerOne变量的对Player实例的引用断开。没有其他的属性或者变量继续引用Player实例,所以它被释放来释放内存。在这发生之前,它的deinitializer自动调用,它的硬币返回给银行。