web-dev-qa-db-ja.com

DeinitはUIViewControllerで呼び出されませんが、Deallocは

Swift deallocと同等のdeinitに相当 のようです。ただし、UIViewControllerでメソッドを定義しようとすると、期待どおりに動作しません...

セットアップ

  1. SwiftまたはObjective-Cのいずれかで、Xcode 7.0を使用して新しいシングルビュープロジェクトを作成します。
  2. ストーリーボードで作成されたView Controllerに「閉じる」ボタンを追加します(このView ControllerをVC2と呼びます。クラスはViewControllerです)。
  3. 新しいView Controllerを追加し、それを初期View Controller(VC1、クラスはnil)に設定します。
  4. 「存在する」ボタンをVC1に追加し、「現在のモーダル」セグエをVC2に追加します。
  5. VC2のコードで、deinit(Swift)またはdealloc(Objective-C)にブレークポイントを配置します。
  6. VC2では、「閉じる」ボタンのアクションに次の操作を行わせます。

    // Swift:
    presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
    
    // Objective-C:
    [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
    
  7. アプリを実行し、両方のボタンをタップして最初にVC2を表示してから、それを閉じます。

Objective-Cdeallocブレークポイントがヒットする方法に注意してください

一方、Swiftでは、deinitブレークポイントはヒットしません

deinitが呼び出されないのはなぜですか?これはバグですか、それとも設計によるものですか?

これが仕様によるものである場合、View Controllerが不要になったときにリソースを解放するためにクリーンアップコードをどこに配置すればよいですか? (このメソッドは廃止されているため、viewDidUnloadに含めることはできません。viewDidDisappearに含めることはできません。他の何かがその参照を保持している可能性があり、最終的に再び表示されるからです。)


注:Swiftでdeallocメソッドを定義しようとすると、次のエラーが表示されます。

Objective-Cセレクター 'dealloc'を持つメソッド 'dealloc()'は、同じObjective-Cセレクターを持つdeinitializerと競合します。

Swift View ControllerがObjective-Cコントローラから継承し、Objective-Cのdeallocメソッドにブレークポイントを設定すると、上記で定義したバグのある同じ動作になります:deinitは呼び出されませんが、deallocは呼び出されます。

割り当てを使用してメモリ内のクラスのインスタンスの数を表示しようとすると、両方のバージョンで同じことが表示されます:# Persistentは常に1で、# Transientは、2番目のView Controllerを表示するたびに増加します。

上記の設定を考えると、View Controllerを保持する 強参照サイクル はないはずです。

19
Senseful

TLDR:

ブレークポイントは、その前に実行可能なコード行がある場合にのみdeinitで機能します。

  • 実行可能コードの行にブレークポイントを配置すると、機能します。
  • 実行可能なコード行は、deinitメソッドに属している必要があります。

私を正しい方向に向けてくれたアダム に感謝します。大規模なテストは行いませんでしたが、deinitでのブレークポイントの動作は、コードの他の場所とは異なるようです。

each行番号にブレークポイントを追加した例をいくつか示します。動作するもの(実行の一時停止、メッセージの記録などのアクションの実行など)は、➤記号で示されます。

通常、メソッドが何もしない場合でも、ブレークポイントは自由にヒットします。

_➤ 1
➤ 2  func doNothing() {
➤ 3 
➤ 4  }
  5
_

ただし、空白のdeinitメソッドでは、[〜#〜] no [〜#〜]ブレークポイントがヒットします。

_  1
  2  deinit {
  3 
  4  }
  5
_

コードをさらに追加すると、ブレークポイントの後に実行可能なコード行があるかどうかに依存していることがわかります。

_➤ 1 
➤ 2  deinit {
➤ 3      //
➤ 4      doNothing()
➤ 5      //
➤ 6      foo = "abc"
  7      //
  8  }
  9
_

特に、7行目と8行目に注意を払ってください。これは、doNothing()の動作とは大きく異なるためです。

doNothing()で4行目のブレークポイントがどのように機能するかのこの動作に慣れた場合、この行で5行目(または4行目)にのみブレークポイントがある場合、コードが実行されていないと誤って推測する可能性があります例:

_➤ 1  
➤ 2  deinit {
➤ 3      number++
  4  //    incrementNumber()
  5  }
  6
_

注:同じ行で実行を一時停止するブレークポイントの場合、作成された順にヒットします。それらの順序をテストするために、ブレークポイントをLog Messageおよびアクションの評価後に自動的に続行に設定します。

注:私のテストでは、別の潜在的な落とし穴もありました。print("test")を使用すると、デバッグ領域がポップアップしてメッセージが表示されます(メッセージは太字で表示されます)。ただし、ブレークポイントを追加してLog Messageに通知すると、通常のテキストでログに記録され、not デバッグ領域をポップして開きます。出力を表示するには、デバッグ領域を手動で開く必要があります。

注:これはすべてXcode 7.1.1でテストされました

30
Senseful

私はまだ試していませんが、あなたのために this を見つけました:

Swiftの最適化段階の一部であるdeinit(奇妙な)内に何らかのコードを配置しない限り、関数は呼び出されないようです。

提案されたとおりにdeinit内にprintステートメントを入れて、結果を報告してください

5
Adam Campbell