Swift 中的deinit 到底是什么鬼

2,639 阅读3分钟

deinit , 中文名叫做析构器。 与之对应的玩意叫 init(构造器)。

我们先上代码:

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 recieve(coins: Int) {
        coinsInBank += coins
    }
    
}
class Player {
    var coinsInpurse: Int
    init(coins: Int) {
        coinsInpurse = Bank.distribute(coins: coins)
    }
    func win(coins: Int) {
        coinsInpurse += Bank.distribute(coins: coins)
    }
    deinit {
        Bank.recieve(coins: coinsInpurse)
    }
}

这里我们定义了一个银行类, coinsInBank这个存储属性 代表银行里存了多少钱。

distribute 方法用来往外出钱, 当它被实例化对象调用后, 实例化对象想要多少钱,我们在这个方法内和银行存款进行比较,如果银行余额大于要的钱,我们就返回要的钱,如果银行余额少于要的钱,我们就把银行总余额给return出去。

recieve 这个方法用来增加银行存款。

另一个Player类,代表玩家。 coinsInpurse 代表他一开始在实例化的时候向银行拿了多少钱。 也就是:

init(coins: Int) {
        coinsInpurse = Bank.distribute(coins: coins)
    }

我们来实例化对象看一看:


var player1: Player? = Player(coins: 500)
        print("A new player has joined the game with \(player1!.coinsInpurse) coins")
        print("There are now \(Bank.coinsInBank) coins left in the bank")

打印结果是:

A new player has joined the game with 500 coins
There are now 9500 coins left in the bank

可以看到现在实例化玩家时,银行类被实例化,银行初始余额一万块,大于玩家要的500 , 玩家以500 块的资金开始游戏。 银行余额剩余9500.

我们让他赢点钱:


player1!.win(coins: 800)
        print("The bank now has \(Bank.coinsInBank) coins")
        print(player1!.coinsInpurse)
        player1 = nil
        print(player1?.coinsInpurse)
        print("PlayerOne has left the game")
        print("The bank now has \(Bank.coinsInBank) coins")

打印结果是:

The bank now has 8700 coins
1300
nil
PlayerOne has left the game
The bank now has 10000 coins

我们可以看到,当 player1 = nil 的时候,变量player1 对Player 实例的引用失效,该实例就会被释放,内存回收。 在这之前,这个实例的析构器被自动调用, 执行

Bank.recieve(coins: coinsInpurse)

玩家的硬币被返还给银行。

OK 看完上面的代码我们再来总结一下deinit这个玩意:

  1. 在类的定义中,每个类最多只能有一个析构器,而且析构器不带任何参数
  2. 析构器在实例释放之前被自动调用,析构器是不允许被主动调用的。
  3. 子类继承了父类的析构器,并且在子类析构器实现的最后,父类的析构器会被自动调用。即使子类没有提供自己的析构器,父类的析构器也同样会被调用。
  4. 通常你不需要使用deinit,当你的实例化对象不在使用时,系统会自动帮你管理内存,但一些自定义的情况会涉及自己手动deinit, 例如下面这个我的另一篇文章:

juejin.cn/post/689410…