Perl 「untie attempted while 1 inner references still exist」ワーニングの原因


Perl で tie を使った後 untie 時にどうしても「untie attempted while 1 inner references still exist」のワーニングが消えない事がある。意外なほど単純だったその理由。

結論

tied を使うとワーニングが出る

tie のワーニングに関する基本事項

Perl で、tie 時にその戻り値を変数で受けてしまうと、untie の前にその変数を undef してやらないとこのワーニングが出る。

ワーニングが出る例

use strict;
use warnings;
use DB_File;
my %database;

my $x = tie(%database, “DB_File”, “hoge.dbm”, O_RDWR|O_CREAT, 0644, $DB_BTREE)
or die $!;

#何かの処理

untie %database;

このような場合は、untie する前に変数 $x を undef すればこの問題を回避できる。

ワーニングが出ない例

use strict;
use warnings;
use DB_File;
my %database;

my $x = tie(%database, “DB_File”, “hoge.dbm”, O_RDWR|O_CREAT, 0644, $DB_BTREE)
or die $!;

#何かの処理

undef $x;
untie %database;

変数を使っていなくても untie でワーニングが出てしまう例

さて所で、そもそも tie する時に変数で受けなければただ untie してもワーニングは出ない。

ワーニングが出ない例 その2

use strict;
use warnings;
use DB_File;
my %database;

tie(%database, “DB_File”, “hoge.dbm”, O_RDWR|O_CREAT, 0644, $DB_BTREE)
or die $!;

#何かの処理

untie %database;

ところが、このような書き方をしてもどうしてもワーニングが消えない事があった。

何故だ何故だとかなりハマったのだが、END サブルーチンでの終了処理として untie していたために以下のように書いていたのが原因だった。

untie でワーニングが出る書き方

END {
untie %database if tied %database;
}

この「if tied %database」の一撃で内部的にリファレンスが生成されてしまうらしい。

もし untie 前に判別をするのであれば、以下のように一旦変数で受けた物を undef してやれば OK。

my $flg = tied %database;
if ( $flg ) {
undef $flg;
untie %database;
}