あまりにも遅くない暗黙的なインタフェース変数について、私は同様のquestionを尋ねました。暗黙のインターフェイス変数のコンパイラ処理は文書化されていますか?
この質問のソースは、コンパイラによって作成された暗黙のインターフェイス変数の存在を認識していないため、私のコードのバグでした。この変数は、それを所有するプロシージャが終了したときに確定されました。これは、変数の寿命が私が予想していたより長いために、バグを引き起こしました。
program ImplicitInterfaceLocals;
{$APPTYPE CONSOLE}
uses
Classes;
function Create: IInterface;
begin
Result := TInterfacedObject.Create;
end;
procedure StoreToLocal;
var
I: IInterface;
begin
I := Create;
end;
procedure StoreViaPointerToLocal;
var
I: IInterface;
P: ^IInterface;
begin
P := @I;
P^ := Create;
end;
begin
StoreToLocal;
StoreViaPointerToLocal;
end.
StoreToLocal
はあなたが想像としてコンパイルされています
は今、私は、コンパイラから、いくつかの興味深い行動を説明するための簡単なプロジェクトを持っています。関数の結果であるローカル変数I
は、暗黙のvar
パラメータとしてCreate
に渡されます。 StoreToLocal
の整頓は、IntfClear
への単一の呼び出しになります。そこに驚きはありません。
しかし、StoreViaPointerToLocal
は異なって扱われます。コンパイラは暗黙のローカル変数を作成し、それをCreate
に渡します。 Create
が返ると、P^
への代入が実行されます。これにより、ルーチンへの参照を保持する2つのローカル変数が残されます。 の整頓は、IntfClear
を2回呼び出します。
StoreViaPointerToLocal
のためのコンパイルされたコードは、このようなものです:
ImplicitInterfaceLocals.dpr.24: begin
00435C50 55 push ebp
00435C51 8BEC mov ebp,esp
00435C53 6A00 push $00
00435C55 6A00 push $00
00435C57 6A00 push $00
00435C59 33C0 xor eax,eax
00435C5B 55 push ebp
00435C5C 689E5C4300 push $00435c9e
00435C61 64FF30 push dword ptr fs:[eax]
00435C64 648920 mov fs:[eax],esp
ImplicitInterfaceLocals.dpr.25: P := @I;
00435C67 8D45FC lea eax,[ebp-$04]
00435C6A 8945F8 mov [ebp-$08],eax
ImplicitInterfaceLocals.dpr.26: P^ := Create;
00435C6D 8D45F4 lea eax,[ebp-$0c]
00435C70 E873FFFFFF call Create
00435C75 8B55F4 mov edx,[ebp-$0c]
00435C78 8B45F8 mov eax,[ebp-$08]
00435C7B E81032FDFF call @IntfCopy
ImplicitInterfaceLocals.dpr.27: end;
00435C80 33C0 xor eax,eax
00435C82 5A pop edx
00435C83 59 pop ecx
00435C84 59 pop ecx
00435C85 648910 mov fs:[eax],edx
00435C88 68A55C4300 push $00435ca5
00435C8D 8D45F4 lea eax,[ebp-$0c]
00435C90 E8E331FDFF call @IntfClear
00435C95 8D45FC lea eax,[ebp-$04]
00435C98 E8DB31FDFF call @IntfClear
00435C9D C3 ret
私は、コンパイラはこれをやっている理由を推測することができます。結果変数に代入しても例外が発生しないことが証明できる場合(つまり変数がローカルの場合)、結果変数を直接使用します。それ以外の場合は、暗黙のローカルを使用し、関数が戻ったらインタフェースをコピーし、例外が発生した場合に参照をリークさせないようにします。
しかし、私はドキュメントでこれについての記述は見つかりません。インタフェースの寿命が重要であり、プログラマーとして機会に影響を与える必要があるため重要です。
この動作のドキュメントがあるかどうかは誰にも分かりますか?誰もそれについてもっと知識を持っていないのであれば?インスタンスフィールドはどのように処理されますか、私はそれをまだチェックしていません。もちろん、私は自分自身でそれを試してみることができますが、私はより正式な声明を探していて、試行錯誤によって実現された実装の詳細に頼るのを常に避けることを好みます。 1
アップデートは、私は別のファイナライズを行う前に、インターフェイスの背後にあるオブジェクトを完成させるために必要なときに、それは私には大事、レミーの質問に答えるために。
begin
AcquirePythonGIL;
try
PyObject := CreatePythonObject;
try
//do stuff with PyObject
finally
Finalize(PyObject);
end;
finally
ReleasePythonGIL;
end;
end;
このように書かれています。しかし実際のコードでは、私はGILがリリースされ、それが爆撃された後に確定された2番目の暗黙のローカルを持っていました。 Acquire/Release GIL内のコードを別のメソッドに抽出して、この問題を解決し、インタフェース変数のスコープを狭めました。
質問が本当に複雑であることを除いて、これがなぜ落とされたのか分かりません。私の頭の上に道を行くためにアップアップされました。私が知っているのは、ちょうど1年前に仕事をしていたアプリでこのarcanumのビットが微妙な参照カウントのバグを引き起こしたことが分かっています。私たちの最高のオタクの1人は、それを理解する時間を費やしました。最終的には、コンパイラがどのように動作しようとしているのか理解できませんでした。 –
参照カウントインターフェイスを操作するときは、独自のインターフェイス参照を持つ他のクライアント(あなたの場合はコンパイラ)がないと想定しないでください。すべてのクライアントが 'AddRef' /' Release'を正しく実行すれば、すべて正常に動作します。さもなければ、それはバグです(あなたのバグはコンパイラが参照カウントを正しく行うと仮定しているからです)。 – kludg
@Sergコンパイラは参照カウントを完全に行いました。問題は、私が見ることができなかった参照を保持する余分な変数があったということでした。私が知りたがっていることは、コンパイラがそのような余分な隠された参照を取ってしまうことです。 –