見出し画像

Swiftでプログラミング- Deinitialization

デイニシャライザは、クラスのインスタンスが解放される直前に呼び出されます。初期化を init キーワードで書くのと同じように、deinit キーワードを使います。デイニシャライザはクラスでのみ使用できます。

How Deinitialization Works デイニシャライザの使い方。

Swiftは、メモリを解放するために、不要になったインスタンスを自動的にメモリを解放します。Swiftは、自動参照カウント(ARC)を通じてインスタンスのメモリ管理を処理します。一般的には、インスタンスが解放されたときに手動でクリーンアップを行う必要はありません。しかし、独自のリソースを使用している場合は、自分で追加のクリーンアップを行う必要があるかもしれません。例えば、ファイルを開いてデータを書き込むカスタムクラスを作成した場合、クラスのインスタンスがメモリを解放される前にファイルを閉じる必要があるかもしれません。

クラス定義では、クラスごとに最大1つのデイニシャライザを持つことができます。デイニシャライザはパラメータを取らず、括弧を付けずに記述します。

   deinit {
       // perform the deinitialization
   }

デイニシャライザは、インスタンスの脱離が行われる直前に自動的に呼び出されます。自分でデイニシャライザを呼び出すことはできません。スーパークラスのデイニシャライザーはサブクラスに継承され、サブクラスのデイニシャライザーの実装の最後にスーパークラスのデイニシャライザーが自動的に呼び出されます。サブクラスが独自のデイニシャライザを提供していない場合でも、スーパークラスのデイニシャライザは常に呼び出されます。

デイニシャライザが呼び出されるまでインスタンスは解放されないため、デイニシャライザは呼び出されたインスタンスのすべてのプロパティにアクセスすることができ、それらのプロパティに基づいて動作を変更することができます (クローズする必要のあるファイルの名前を検索するなど)。

Deinitializers in Action

ここでは、デイニシャライザの動作例をご紹介します。この例では、単純なゲームのために、BankとPlayerという2つの新しい型を定義しています。Bankクラスは架空の通貨を管理するクラスで、流通するコインは1万枚を超えてはいけません。Bankはゲーム内に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:)の2つのメソッドを提供しています。

distribute(coins:)メソッドは、コインを分配する前に、銀行に十分なコインがあるかどうかを確認します。十分なコインがない場合は、Bankは要求された数よりも小さい数を返します(バンクにコインが残っていない場合はゼロを返します)。また、実際に提供されたコインの数を示す整数値を返します。

receive(coins:)メソッドは、受け取ったコインの枚数をバンクのコインストアに戻すだけです。

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)
       }
   }

各プレーヤーのインスタンスは、初期化時にBankから指定された枚数のコインを受け取って初期化されますが、十分な枚数のコインがない場合は、プレーヤーのインスタンスはその枚数よりも少ない枚数を受け取ることができます。

クラスPlayerはwin(coins:)メソッドを定義しており、Bankから一定数のコインを取得し、プレイヤーの財布に追加します。また、クラスPlayerはデイニシャライザを実装しており、Playerインスタンスが解放される直前に呼び出されます。ここでは、プレーヤーのコインをすべてBankに返却しています。

   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枚を要求します。このプレーヤーのインスタンスは、オプションのプレーヤー変数「playerOne」に格納されます。プレイヤーはいつでもゲームを離れることができるので、ここではオプションの変数を使用しています。オプショナル変数では、現在ゲームに参加しているプレーヤーがいるかどうかを把握できます。

playerOneはオプションなので、coinsInPurseプロパティにアクセスしてデフォルトのコイン枚数を表示するときや、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枚のコインを獲得しています。プレイヤーの財布には2,100枚のコインが入っており、銀行には7,900枚のコインが残っています。

   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インスタンスを参照していないので、メモリを解放するためにdeallocatedされます。その直前に自動的にデイニシャライザが呼び出され、コインがBankに返却されます。

この記事が気に入ったらサポートをしてみませんか?