【Delphi】String 型変数で身に覚えのないメモリリークが報告される

# はじめに
Delphi ではプロジェクトファイルの先頭で次のように記述しておくと、アプリケーション終了時に (していれば) メモリリークが報告されます [^1]。

```pascal
ReportMemoryLeaksOnShutdown := True;
```

そこそこ大きなプログラムを書くと、何故か **String** 型変数でメモリリークが報告される事があります。こういう奴です。

```
Unexpected Memory Leak
An unexpected memory leak has occurred. The unexpected small block leaks are:

nn - nn bytes: UnicodeString x 1
```

**See also:**

 - [System.ReportMemoryLeaksOnShutdown (DocWiki)](http://docwiki.embarcadero.com/Libraries/ja/System.ReportMemoryLeaksOnShutdown)


# 原因

例えば次のようなコードがあったとします。

```pascal:SimpleRec1.dpr
program SimpleRec1;

{$APPTYPE CONSOLE}

type
  TUserPassRec = record
    User: Integer;
    Pass: String;
  end;
  PUserPassRec = ^TUserPassRec;

var
  PUP: PUserPassRec;
begin
  ReportMemoryLeaksOnShutdown := True;
  New(PUP);
  try
    PUP^.User := 123;
    PUP^.Pass := 'HogeHoge';
    Writeln(PUP^.User, PUP^.Pass);
  finally
    DisPose(PUP);
  end;
end.
```

このコードはメモリリークを起こしませんが、例えば C / C++ 由来のコードを移植したとしましょう。「ヌル終端文字列は扱いづらいので **String** にした」とかそういうシチュエーションです。メモリ確保/解放関数に別のものが使われているかもしれません。

```pascal:SimpleRec2.dpr
program SimpleRec2;

{$APPTYPE CONSOLE}

type
  TUserPassRec = record
    User: Integer;
    Pass: String;
  end;
  PUserPassRec = ^TUserPassRec;

var
  PUP: PUserPassRec;
begin
  ReportMemoryLeaksOnShutdown := True;
  PUP := GetMemory(SizeOf(TUserPassRec));
  try
    PUP^.User := 123;
    PUP^.Pass := 'HogeHoge';
    Writeln(PUP^.User, PUP^.Pass);
  finally
    FreeMemory(PUP);
  end;
end.
```

このコード例ではレコード型動的変数のメモリが正しく解放されているにも関わらず、メモリリークが報告されると思います。何故かと言うと、**文字列型の変数を未割当て状態にせずにメモリ解放したから**です。

説明になっていないかもしれませんが、Delphi の **String** 型変数は特定の条件下では `未割当て状態にしてからメモリを解放` する必要があります。その条件とは次の 2 つを満たす場合です。

 1. `Dispose()` 以外の方法でメモリを解放した場合。
 2. **String** 型変数の中身が空ではない場合 (未割当て状態でない場合)。

この条件を満たす場合には、メモリ解放前に `Finalize()` を使って **String** 型変数を未割当て状態にしなければなりません。


```pascal:SimpleRec3.dpr
program SimpleRec3;

{$APPTYPE CONSOLE}

type
  TUserPassRec = record
    User: Integer;
    Pass: String;
  end;
  PUserPassRec = ^TUserPassRec;

var
  PUP: PUserPassRec;
begin
  ReportMemoryLeaksOnShutdown := True;
  PUP := GetMemory(SizeOf(TUserPassRec));
  try
    PUP^.User := 123;
    PUP^.Pass := 'HogeHoge';
    Writeln(PUP^.User, PUP^.Pass);
    Finalize(PUP^.Pass); // <- 未割当て状態にする
  finally
    FreeMemory(PUP);
  end;
end.
```

レコード型に **String** 型のフィールドを配置すると、この状況が発生しうるのです。

また、**String** 型フィールドを未割当て状態にするのに、`Finalize()` の代わりに `SetLength()` を使う事もできます。

```pascal
    SetLength(PUP^.Pass, 0); // <- 長さを 0 にする (未割当て状態にする)
```

**See also:**

 - [System.Initialize (DocWiki)](http://docwiki.embarcadero.com/Libraries/ja/System.Initialize)
 - [System.Finalize (DocWiki)](http://docwiki.embarcadero.com/Libraries/ja/System.Finalize)
 - [System.SetLength (DocWiki)](http://docwiki.embarcadero.com/Libraries/ja/System.SetLength)
 - [長い文字列型 - 内部データ形式（Delphi）(Qiita)](http://docwiki.embarcadero.com/RADStudio/ja/%E5%86%85%E9%83%A8%E3%83%87%E3%83%BC%E3%82%BF%E5%BD%A2%E5%BC%8F%EF%BC%88Delphi%EF%BC%89#.E9.95.B7.E3.81.84.E6.96.87.E5.AD.97.E5.88.97.E5.9E.8B)
 - [10.2. New と Dispose - <10> ポインタ型 (標準 Pascal 範囲内での Delphi 入門) (Qiita)](https://qiita.com/ht_deko/items/745ad4a792d277d33831#102-new-%E3%81%A8-dispose)


## 少し複雑な例

上記例では問題解決方法を説明するために、**String** 型のフィールドを未割当状態にしていました。少し複雑な例を見てみましょう。

```pascal:SimpleRec4.dpr
program SimpleRec4;

{$APPTYPE CONSOLE}

type
  TUserPassRec = record
    User: Integer;
    Pass: String;
  end;

  TEmployeeRec = record
    Code: Integer;
    FirstName: String;
    LastName: String;
    UserPass: TUserPassRec;
  end;
  PEmployeeRec = ^TEmployeeRec;

var
  PE: PEmployeeRec;
begin
  ReportMemoryLeaksOnShutdown := True;
  PE := GetMemory(SizeOf(TEmployeeRec));
  try
    PE^.Code := 1;
    PE^.FirstName := 'John';
    PE^.LastName  := 'Smith';
    PE^.UserPass.User := 123;
    PE^.UserPass.Pass := 'HogeHoge';
    Writeln(PE^.UserPass.User, PE^.UserPass.Pass);
    Finalize(PE^.FirstName);     // <- 未割当て状態にする
    Finalize(PE^.LastName );     // <- 未割当て状態にする
    Finalize(PE^.UserPass.Pass); // <- 未割当て状態にする
  finally
    FreeMemory(PE);
  end;
end.
```

コード例のように **String** 型のフィールドを一つずつ未割当て状態にしてもいいのですが、`Finalize()` にレコード型の動的変数を指定すれば **String** 型のフィールドすべてを一気に未割当て状態にする事ができます。

```pascal
...
    Writeln(PE^.UserPass.User, PE^.UserPass.Pass);
    Finalize(PE^); // <- 未割当て状態にする (動的変数指定)
  finally
  ...
```

但し、この時 `Finalize()` にレコード型ポインタ変数を渡してはいけません。次の例は誤りです。

```pascal
...
    Writeln(PE^.UserPass.User, PE^.UserPass.Pass);
    Finalize(PE); // <- × 未割当て状態にする (ポインタ変数指定)
  finally
  ...
```


# おわりに
レコード型を動的変数として使う場合には、由緒正しく `New()` と `Dispose()` を使いましょう、というお話でした。

**See also:**

 - [メモリと文字列バッファ確保の関数 (Mr.XRAY)](http://mrxray.on.coocan.jp/Delphi/Others/MemAlloc.htm)
 - [【Delphi】構造体と文字列とポインタ (Qiita)](https://qiita.com/ht_deko/items/87aee1c7aee75aba90a2)

[^1]: 素の状態で使えるのは Delphi 2006 (BDS 2006 / Turbo Delphi) から。