Main Content

関数完了時のクリーンアップ

概要

他のプログラム コードに干渉しないように、プログラム環境をクリーンアップすることを心がけてください。たとえば、次のようなクリーンアップを実行します。

  • インポートまたはエクスポートのために開いたファイルを閉じる

  • MATLAB® パスを復元する

  • MATLAB 関数または MEX ファイルの削除を阻止または許可するために、メモリをロックまたはロック解除する

  • 作業フォルダーを変更した場合には、既定の状態に戻す

  • グローバル変数と永続変数が適切な状態にあることを確認する

MATLAB では、このために関数 onCleanup が用意されています。この関数をプログラム内で使用すると、その関数のクリーンアップ ルーチンが設定されます。通常終了か、エラーまたは Ctrl+C による終了で、この関数が終了すると、MATLAB によって自動的にクリーンアップ ルーチンが実行されます。

次のステートメントは、現在実行中のプログラムに対してクリーンアップ ルーチン cleanupFun を設定します。

cleanupObj = onCleanup(@cleanupFun);

プログラムが存在する場合は、MATLAB によって onCleanup クラスのインスタンスが検出され、関連付けられた関数ハンドルが実行されます。関数クリーンアップを生成し、アクティブ化するプロセスは、以下の手順で実行されます。

  1. 開発中のプログラムのクリーンアップ ルーチンを 1 つ以上作成します。ここでは、そのようなルーチンは 1 つだけと想定します。

  2. クリーンアップ ルーチン用関数ハンドルを作成します。

  3. プログラム コードの前半のどこかで、関数 onCleanup の呼び出しを挿入し、関数ハンドルを渡します。

  4. プログラムが実行されると、onCleanup の呼び出しによって、手順 1 で作成したクリーンアップ ルーチンのハンドルを含むクリーンアップ オブジェクトが作成されます。

  5. プログラムが終了すると、ローカル変数であるすべてのオブジェクトが MATLAB によって暗黙的にクリアされます。これによって、プログラムの各ローカル オブジェクト (手順 4 で作成したクリーンアップ オブジェクトを含む) のデストラクター メソッドが呼び出されます。

  6. このオブジェクトのデストラクター メソッドが存在する場合は、そのメソッドにより、このルーチンが呼び出されます。これにより、プログラミング環境の復元に必要なタスクが実行されます。

プログラム ファイルに対して、任意の数のクリーンアップ ルーチンを宣言できます。onCleanup に対する各呼び出しにより、返されたクリーンアップ オブジェクトごとに別個のクリーンアップ ルーチンが設定されます。

何らかの理由で、onCleanup から返されたオブジェクトがプログラムの有効期間を超えて存続する場合は、そのオブジェクトに関連付けられているクリーンアップ ルーチンが関数の終了時に実行されることはありません。代わりに、そのクリーンアップ ルーチンは、オブジェクトが (オブジェクト変数のクリアにより) 破壊されると実行されます。

クリーンアップ ルーチンを、そのルーチン外で定義された変数に依存させないでください。たとえば以下の例で、左側の入れ子関数が実行されてもエラーは発生しませんが、非常によく似た右側の関数ではエラーとなり、"関数または変数 'k' が未定義です" というメッセージが表示されます。これは、入れ子されたクリーンアップ ルーチンの外側で定義されている変数 k にクリーンアップ ルーチンが依存しているからです。

function testCleanup               function testCleanup
k = 3;                             k = 3;
myFun                              obj = onCleanup(@myFun);
    function myFun                     function myFun
    fprintf('k is %d\n', k)            fprintf('k is %d\n', k)
    end                                end
end                                end

終了時にプログラムをクリーンアップする例

例 1 — 終了時に開いているファイルを閉じる

MATLAB は、関数 openFileSafely が終了すると、識別子 fid をもつファイルを閉じます。

function openFileSafely(fileName)
fid = fopen(fileName, 'r');
c = onCleanup(@()fclose(fid));

s = fread(fid);
     .
     .
     .
end

例 2 — 選択したフォルダーを保持する

この例では、functionThatMayError がエラーを返したかどうかにはかかわらず、現在のフォルダーが保持されます。

function changeFolderSafely(fileName)
   currentFolder = pwd;
   c = onCleanup(@()cd(currentFolder));

   functionThatMayError;
   end   % c executes cd(currentFolder) here.

例 3 — Figure を閉じて MATLAB パスを復元する

この例では、toolbox\images フォルダー内のファイルを含めるように MATLAB パスを拡張し、これらのフォルダーの 1 つから Figure を表示します。Figure の表示後、クリーンアップ ルーチン restore_env が Figure を閉じ、パスを元の状態に復元します。

function showImageOutsidePath(imageFile)
fig1 = figure;
imgpath = genpath([matlabroot '\toolbox\images']);

% Define the cleanup routine.
cleanupObj = onCleanup(@()restore_env(fig1, imgpath));

% Modify the path to gain access to the image file, 
% and display the image.
addpath(imgpath);
rgb = imread(imageFile);
fprintf('\n   Opening the figure %s\n', imageFile);
image(rgb);
pause(2);

   % This is the cleanup routine.
   function restore_env(fighandle, newpath)
   disp '   Closing the figure'
   close(fighandle);
   pause(2)
   
   disp '   Restoring the path'
   rmpath(newpath);
   end
end

以下のように関数を実行してください。関数の実行前と実行後のパスの長さを比較することにより、パスが復元されたことを確認できます。

origLen = length(path);

showImageOutsidePath('greens.jpg')
   Opening the figure greens.jpg
   Closing the figure
   Restoring the path

currLen = length(path);
currLen == origLen
ans =
     1

クリーンアップ ルーチンに関する情報の取得

上記の例 3 では、クリーンアップ ルーチンとその呼び出しに必要なデータは、無名関数のハンドルに含まれています。

@()restore_env(fig1, imgpath)

そのハンドルの詳細は、関数 onCleanup が返すオブジェクトに含まれています。

cleanupObj = onCleanup(@()restore_env(fig1, imgpath));

以下に示すようにクリーンアップ オブジェクトの task プロパティを使用して、これらの詳細にアクセスできます (「% This is the cleanup routine.」というコメント行の直前に以下のコードを追加することにより、関数 showImageOutsidePath を変更してください)。

disp '   Displaying information from the function handle:'
task = cleanupObj.task;
fun = functions(task)
wsp = fun.workspace{2,1}
fprintf('\n');
pause(2);

変更した関数を実行して、functions コマンドの出力と workspace セルの 1 つのコンテンツを表示します。

showImageOutsidePath('greens.jpg')

Opening the figure greens.jpg
Displaying information from the function handle:
fun = 
     function: '@()restore_env(fig1,imgpath)'
         type: 'anonymous'
         file: 'c:\work\g6.m'
    workspace: {2x1 cell}
wsp = 
     imageFile: 'greens.jpg'
          fig1: 1
       imgpath: [1x3957 char]
    cleanupObj: [1x1 onCleanup]
           rgb: [300x500x3 uint8]
          task: @()restore_env(fig1,imgpath)

Closing the figure
Restoring the path

onCleanup の使用と try/catch の使用の比較

関数の異常終了時にクリーンアップ ルーチンを実行するもう 1 つの方法は、try, catch ステートメントの使用です。ただし、このテクニックの使用には制限があります。Ctrl+C の入力によってプログラムを終了すると、ただちに、MATLAB が try ブロックを終了するので、クリーンアップ ルーチンはまったく実行されません。クリーンアップ ルーチンは、関数を正常に終了した場合も実行されません。

次のプログラムでは、エラーが発生するとクリーンアップが実行されますが、Ctrl + C が入力されてもクリーンアップは実行されません。

function cleanupByCatch
try
    pause(10);
catch
    disp('   Collecting information about the error')
    disp('   Executing cleanup tasks')
end

try/catch ステートメントとは異なり、関数 onCleanup は、プログラムの正常終了時や任意のエラーがスローされたときだけでなく、Ctrl+C キーの入力時にも実行されます。次の例では、try/catchonCleanup に置き換えます。

function cleanupByFunc
obj = onCleanup(@()...
    disp('   Executing cleanup tasks'));
pause(10);

onCleanup をスクリプトで使用する場合

onCleanup をスクリプトで使用すると、関数で使用する場合と機能が異なります。関数で使用する場合、クリーンアップ オブジェクトが関数ワークスペースに保存されます。関数が終了すると、このワークスペースはクリアされるので、関連付けられているクリーンアップ ルーチンが実行されます。スクリプトで使用する場合、クリーンアップ オブジェクトは、ベース ワークスペース (つまり、コマンド プロンプトで行われる対話式作業で使用するワークスペース) に保存されます。スクリプトの終了は、このベース ワークスペースには影響しないので、クリーンアップ オブジェクトはクリアされず、そのオブジェクトに関連付けられたクリーンアップ ルーチンは実行されません。このタイプのクリーンアップ メカニズムをスクリプトで使用するには、最初のスクリプトの終了時に、コマンド ラインまたは別のスクリプトから明示的にオブジェクトをにクリアしなければなりません。