ハンドル クラスのデストラクター
基礎知識
"クラス デストラクター" – ハンドル クラスのオブジェクトを破棄する前に MATLAB® が暗黙的に呼び出す delete
と名付けられたメソッドです。ユーザー定義のコードで delete
を明示的に呼び出して、オブジェクトを破棄することもできます。
"無効なデストラクター" – 有効なデストラクターの構文要件を満たさない delete
と名付けられたメソッドです。そのため、MATLAB はハンドル オブジェクトを破棄するときに、このメソッドを暗黙的に呼び出しません。値クラスの delete
という名前のメソッドはデストラクターではありません。HandleCompatible
属性を true
に設定する値クラスの delete
という名前のメソッドは、デストラクターではありません。
ハンドル クラスのデストラクター メソッドの構文
MATLAB は、クラスのオブジェクトを破棄するときにハンドル クラスのデストラクターを呼び出します。delete
が適切な構文で通常のメソッドとして定義されている場合にのみ、MATLAB は delete
と名付けられたメソッドをクラス デストラクターとして認識します。
delete
メソッドが有効なクラス デストラクターであるための条件は、次のとおりです。
クラス オブジェクトであるスカラー入力引数を 1 つ定義していなければならない
出力引数を定義しない
Sealed
、Static
またはAbstract
ではない入力引数の検証に
arguments
ブロックを使用できない
さらに、delete
メソッドは次のことを "行いません"。
オブジェクトが無効な場合でも、エラーをスローする
破棄されているオブジェクトに新しいハンドルを作成する
サブクラスのメソッドを呼び出すまたはプロパティにアクセスする
クラスのオブジェクトを破棄する場合、MATLAB は非準拠の delete
メソッドは呼び出しません。非準拠の delete
メソッドがあると、handle
クラスの delete メソッドの優先順位が低くなり、オブジェクトの破棄が妨げられる場合があります。
ハンドル互換の値クラスで定義された delete
メソッドは、ハンドル サブクラスによって delete
メソッドが継承された場合でも、デストラクターではありません。ハンドル互換クラスの詳細については、ハンドル互換クラスを参照してください。
delete
を通常のメソッドとして宣言するには、次のようにします。
methods function delete(obj) % obj is always scalar ... end end
配列の要素単位で呼び出される delete
MATLAB は、delete
メソッドを配列内の要素ごとに別々に呼び出します。このため、delete
メソッドには、呼び出しごとに 1 つのスカラー引数のみが渡されます。
削除されたハンドルに delete
を呼び出してもエラーにはならず、何も処理は行われません。この設計により delete
は、有効なオブジェクトと無効なオブジェクトが混在したオブジェクト配列を操作できるようになります。
delete
メッソドを実行している間のハンドル オブジェクト
オブジェクトに対して delete
メソッドを呼び出すと、必ずそのオブジェクトが破棄されます。MATLAB コードで delete
の呼び出しが明示的に行われるか、または MATLAB によって呼び出されると、オブジェクトがどのワークスペースからもアクセスできなくなるため、そのオブジェクトが破棄されます。一度 delete
メソッドを呼び出すと、オブジェクトの破棄を中止したり止めたりすることはできません。
delete
メソッドは、削除中のオブジェクトのプロパティにアクセスできます。MATLAB では、delete
メソッドによってオブジェクトのクラスやすべてのスーパークラスへの実行が完了するまで、それらのプロパティは破棄されません。
delete
メソッドが、削除されるオブジェクトへのハンドルを含む新しい変数を作成した場合、それらのハンドルは無効です。delete
メソッドが実行を終了すると、いずれのワークスペースのいずれの変数でも削除されたオブジェクトへのハンドルが無効になります。
メソッドの呼び出し時にオブジェクトの破棄が開始されるため、isvalid
メソッドは delete
メソッド内のハンドル オブジェクトに対して false
を返します。
MATLAB は、作成順序と逆に delete
メソッドを呼び出します。つまり、MATLAB は、スーパークラスの delete
メソッドの前にサブクラスの delete
メソッドを呼び出します。
スーパークラスで、プロパティがサブクラスによって管理されることが仮定されている場合、スーパークラスからその delete
メソッドでプロパティにアクセスすべきではありません。たとえば、サブクラスが、継承した抽象プロパティを使用してオブジェクト ハンドルを格納する場合、サブクラスでは delete
メソッド内でこのオブジェクトを破棄する必要がありますが、スーパークラスでは delete
メソッド内でそのプロパティにアクセスすべきではありません。
部分的に作成されたオブジェクトの破棄のサポート
オブジェクトの作成中に発生するエラーが原因で、オブジェクトが完全に作成される前に delete
が呼び出されることがあります。そのため、クラスの delete
メソッドは部分的に作成されたオブジェクトを扱えなければなりません。
たとえば、PartialObject
クラスの delete
メソッドは、Data
プロパティに含まれているデータにアクセスする前にこのプロパティが空であるかどうかを判別します。コンストラクター引数を Name
プロパティに割り当てる際にエラーが発生した場合、MATLAB は部分的に作成されたオブジェクトを delete に渡します。
classdef PartialObject < handle properties % Restrict the Name property % to a cell array Name cell Data end methods function h = PartialObject(name) if nargin > 0 h.Name = name; h.Data.a = rand(10,1); end end function delete(h) % Protect against accessing properties % of partially constructed objects if ~isempty(h.Data) t = h.Data.a; disp(t) else disp('Data is empty') end end end end
必要な cell 配列の代わりに char
ベクトルを指定してコンストラクターを呼び出すと、エラーが発生します。
obj = PartialObject('Test')
MATLAB は部分的に作成されたオブジェクトを delete
メソッドに渡します。Name
プロパティの設定中にエラーが発生したため、コンストラクターは Data
プロパティの値を設定していません。
Data is empty Error setting 'Name' property of 'PartialObject' class: ...
デストラクター メソッドを定義するタイミング
delete
メソッドを使用して、MATLAB がオブジェクトを破棄する前にクリーンアップ操作を実行します。MATLAB は、Ctrl+C やエラーにより実行が中断される場合でも、確実に delete
メソッドを呼び出します。
ハンドル クラス作成中にエラーが発生すると、MATLAB は、プロパティと初期化された基底クラスに含まれる任意のオブジェクトのデストラクターと共に、クラス デストラクターをオブジェクトに呼び出します。
たとえば、メソッドで書き込み用のファイルを開き、そのファイルを delete
メソッドで閉じるとします。delete
メソッドでは、オブジェクトがその FileID
プロパティに格納するファイル識別子で fclose
を呼び出すことができます。
function delete(obj) fclose(obj.FileID); end
クラス階層内のデストラクター
クラスの階層を作成すると、各クラスで独自の delete
メソッドを定義できます。MATLAB は、オブジェクトを破棄するときに階層内の各クラスの delete
を呼び出します。handle
サブクラスで delete
メソッドを定義しても、handle
クラスの delete
メソッドはオーバーライドされません。サブクラスの delete
メソッドはスーパークラスの delete
メソッドを拡張します。
シールされた Delete メソッドの継承
デストラクターを Sealed
にすると、有効なデストラクターを定義できません。Sealed
delete
メソッドを定義するクラスをインスタンス化しようとすると、MATLAB からエラーが返されます。
通常、Sealed
としてメソッドを宣言すると、サブクラスでそのメソッドをオーバーライドできなくなります。ただし、有効なデストラクターではない delete
という名前の Sealed
メソッドは、サブクラスが独自のデストラクターを定義することを防ぎません。
たとえば、有効なデストラクターではないが Sealed
である delete
という名前のメソッドをスーパークラスで定義した場合、サブクラスは次のようになります。
有効なデストラクターを定義できる (名前は必ず
delete
とする)。delete
という名前のメソッドを無効なデストラクターとして定義できない。
異種混合階層にあるデストラクター
異種混合クラス階層では、異種混合配列が渡されるすべてのメソッドはシールされていなければなりません。ただし、この規則はクラス デストラクター メソッドには適用されません。デストラクター メソッドはシールできないので、シールされてはいないがデストラクターとして機能する有効なデストラクターを、異種混合階層で定義できます。
異種混合階層については、異種混合クラス階層の設計を参照してください。
オブジェクトのライフサイクル
MATLAB は、オブジェクトのライフサイクルの終了時に、delete
メソッドを呼び出します。オブジェクトのライフサイクルは、そのオブジェクトが以下の場合に終了します。
どこからも参照されていない
ハンドルに対して
delete
を呼び出すことによって明示的に削除される
関数の内部
ローカル変数または入力引数によって参照されるオブジェクトのライフサイクルは、その変数が代入されたときから、再び代入されたり、クリアされたり、あるいはその関数内または任意のハンドル配列内での参照が行われなくなるまで存続します。
変数は、明示的にクリアされるかその関数が終了すると、スコープの外に出ます。変数がスコープ外になり、その値が delete
メソッドを定義するハンドル クラスに属する場合は、MATLAB によってそのメソッドが呼び出されます。MATLAB は、関数内の変数の順序を定義しません。複数の値が同じ関数にある場合、MATLAB がそれぞれの値を破棄する順序は予測できません。
ハンドル オブジェクトを破棄する一連の過程
MATLAB は、オブジェクトを破棄する場合、以下のシーケンスで delete
メソッドを呼び出します。
オブジェクトのクラスの
delete
メソッド各スーパークラスの
delete
メソッドは、直上のスーパークラスから始め、最も一般のスーパークラスまで階層を上がります。
MATLAB は、クラス定義に指定した順序で、同じ階層レベルでスーパークラスの delete
メソッドを呼び出します。たとえば、以下のクラス定義では supclass1
が supclass2
の前に指定されています。MATLAB は、supclass1
の delete
メソッドを supclass2
の delete
メソッドの前に呼び出します。
classdef myClass < supclass1 & supclass2
各 delete
メソッドの呼出し後に、MATLAB は、メソッドが呼び出されたクラスに属するプロパティ値を破棄します。ただし、クラスのスコープ外でまだ参照されているハンドル オブジェクトがプロパティ値に含まれている場合、含まれているオブジェクトに対して delete
を呼び出しても、プロパティのハンドル オブジェクト自体は削除されません。他の既存の参照からは引き続きアクセスできます。
スーパークラスの delete
メソッドは、サブクラスに属する、メソッドの呼び出しやプロパティへのアクセスができません。
循環参照のあるオブジェクトの破棄
ある一連のオブジェクトが、巡回的なグラフを形成する一連のオブジェクトを参照しているとします。この場合、MATLAB は以下の動作をします。
循環内でのみ参照されているオブジェクトを破棄する
循環外の MATLAB 変数から任意のオブジェクトに対して外部参照がある場合は、オブジェクトを破棄しない
MATLAB は、作成順序とは逆の順番でオブジェクトを破棄します。詳細については、delete メッソドを実行している間のハンドル オブジェクトを参照してください。
オブジェクトの delete メソッドへのアクセスの制限
オブジェクトで次のように delete
を明示的に呼び出して、ハンドル オブジェクトを破棄します。
delete(obj)
クラスは、delete
メソッドの Access
属性を private
に設定して、オブジェクトの明示的な破棄を回避できます。ただし、クラスのメソッドは private
delete
メソッドを呼び出すことができます。
クラスの delete
メソッド Access
属性が protected
である場合、そのクラスおよびサブクラスのメソッドのみがそのクラスのオブジェクトを明示的に削除できます。
ただし、オブジェクトのライフサイクルが終了すると、MATLAB はメソッドの Access
属性とはかかわりなく、オブジェクトの破棄に際してオブジェクトの delete
メソッドを呼び出します。
継承されたプライベートな Delete メソッド
クラス デストラクターの動作は、オーバーライドされたメソッドの通常の動作とは異なります。MATLAB は、delete
メソッドが public
でない場合も含め、破棄するときに各スーパークラスのそれぞれの delete
メソッドを実行します。
オブジェクトの delete
メソッドを明示的に呼び出すと、MATLAB はオブジェクトを定義しているクラスで delete
メソッドの Access
属性をチェックしますが、オブジェクトのスーパークラス内ではチェックしません。private
な delete
メソッドのあるスーパークラスは、サブクラスのオブジェクトの破棄を回避できません。
シールされたクラスには、プライベートな delete メソッドを宣言することが最も論理的です。クラスがシールされていない場合、サブクラスはパブリックのアクセスを指定して独自の delete メソッドを定義できます。パブリック サブクラス delete
メソッドが明示的に呼び出される結果として、MATLAB がスーパークラスの delete
プライベート メソッドを呼び出すためです。
無効なデストラクター Delete メソッド
クラスは有効なクラス デストラクターではない delete
という名前のメソッドを実装できます。MATLAB は、オブジェクトを破棄するときにこのメソッドを暗黙的に呼び出しません。この場合、delete
は通常のメソッドのように動作します。
たとえば、スーパークラスが無効なデストラクターである delete
と名付けられた Sealed
メソッドを実装する場合、MATLAB はサブクラスがこのメソッドをオーバーライドすることを許可しません。
値クラスで定義された delete
メソッドは、クラス デストラクターになることはできません。
MATLAB オブジェクトへの外部参照
MATLAB は、オブジェクトのライフサイクルのうち、外部言語によって独自にオブジェクトのライフサイクル管理 (つまり、ガーベジ コレクション) が実行されるものを管理しません。外部環境は MATLAB にいつ外部参照が破棄されたかを通知しないため、MATLAB は循環参照で使用されるオブジェクトをいつ安全に破棄できるかを検出できません。
MATLAB オブジェクトへの外部参照を回避できない場合は、MATLAB でオブジェクトを破棄することにより循環参照を明示的に解消します。
次の節では、MATLAB オブジェクトを参照する Java® オブジェクトを使用するときにこの状況を管理する方法を説明します。
デストラクターの実行を回避できる Java 参照
Java は、MATLAB オブジェクトが使用するオブジェクトのデストラクターをサポートしません。このため、Java オブジェクトと MATLAB オブジェクトの両方を含むアプリケーションで使用されるすべてのオブジェクトのライフサイクルを管理することが重要です。
MATLAB オブジェクトへの参照を保持する Java オブジェクトは MATLAB オブジェクトの削除を回避できます。この場合、MATLAB は、そのオブジェクトを参照するハンドル変数がない場合でも、ハンドル オブジェクトの delete
メソッドを呼び出しません。delete
メソッドを確実に実行するには、ハンドル変数がスコープ外になる前に、オブジェクトで明示的に delete
を呼び出します。
MATLAB オブジクトを参照する Java オブジェクトのコールバックを定義するときに問題が起きる可能性があります。
たとえば、CallbackWithJava
クラスが Java com.mathworks.jmi.Callback
オブジェクトを作成し、クラス メソッドをコールバック関数として割り当てます。その結果、関数ハンドル コールバック経由でのハンドル オブジェクトへの参照をもつ Java オブジェクトが生成されます。
classdef CallbackWithJava < handle methods function obj = CallbackWithJava jo = com.mathworks.jmi.Callback; set(jo,'DelayedCallback',@obj.cbFunc); % Assign method as callback jo.postCallback end function cbFunc(obj,varargin) c = class(obj); disp(['Java object callback on class ',c]) end function delete(obj) c = class(obj); disp(['ML object destructor called for class ',c]) end end end
関数内で CallbackWithJava
オブジェクトを作成するとします。
function testDestructor cwj = CallbackWithJava ... end
CallbackWithJava
クラスのインスタンスを作成すると、com.mathworks.jmi.Callback
オブジェクトが作成され、コールバック関数が実行されます。
testDestructor
cwj = CallbackWithJava with no properties. Java object callback on class CallbackWithJava
ハンドル変数 cwj
は、関数ワークスペース内にのみ存在します。ただし、MATLAB は、この関数の終了時にクラス delete
メソッドを呼び出しません。com.mathworks.jmi.Callback
オブジェクトはそのまま存在し、CallbackWithJava
クラスのオブジェクトへの参照を保持します。これにより、MATLAB オブジェクトの破棄が回避されます。
clear classes
Warning: Objects of 'CallbackWithJava' class exist. Cannot clear this class or any of its superclasses.
アクセスできないオブジェクトの発生を避けるために、MATLAB オブジェクトのハンドルを失う前に delete
を明示的に呼び出します。
function testDestructor cwj = CallbackWithJava ... delete(cwj) end
アプリケーションでのオブジェクトのライフサイクルの管理
Java またはその他の外部言語のオブジェクトを使用する MATLAB アプリケーションは、関連するオブジェクトのライフサイクルを管理する必要があります。一般的なユーザー インターフェイス アプリケーションは MATLAB オブジェクトから Java オブジェクトを参照し、MATLAB オブジェクトを参照する Java オブジェクトでコールバックを作成します。
これらの循環参照は、以下に示すさまざまな方法で解消できます。
不要になった MATLAB オブジェクトに対して
delete
を明示的に呼び出すMATLAB オブジェクトを参照する Java オブジェクトのコールバックの登録を解除する
Java コールバックと MATLAB オブジェクトの両方を参照する中間ハンドル オブジェクトを使用する