얼마 전에 암시 적 인터페이스 변수에 대해 비슷한 질문을했습니다 .
이 질문의 원인은 컴파일러에 의해 생성 된 암시 적 인터페이스 변수의 존재를 모르기 때문에 내 코드의 버그였습니다. 이 변수는 자신을 소유 한 프로 시저가 완료 될 때 완성되었습니다. 결과적으로 변수의 수명이 예상보다 길어 버그가 발생했습니다.
이제 컴파일러의 흥미로운 동작을 설명하는 간단한 프로젝트가 있습니다.
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^
수행됩니다. 그러면 인터페이스에 대한 참조를 보유하는 두 개의 로컬 변수가있는 루틴이 남습니다. 정리 StoreViaPointerToLocal
하면 IntfClear
.
에 대한 컴파일 된 코드 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
Remy의 질문에 답하기 위해 다른 마무리 작업을 수행하기 전에 인터페이스 뒤의 객체를 마무리해야 할 때가 중요했습니다.
begin
AcquirePythonGIL;
try
PyObject := CreatePythonObject;
try
//do stuff with PyObject
finally
Finalize(PyObject);
end;
finally
ReleasePythonGIL;
end;
end;
이렇게 써도 괜찮습니다. 그러나 실제 코드에는 GIL이 출시되고 폭격을받은 후에 완성 된 두 번째 암시 적 로컬이 있습니다. Acquire / Release GIL 내부의 코드를 별도의 메서드로 추출하여 문제를 해결하여 인터페이스 변수의 범위를 좁혔습니다.