Saya mengajukan pertanyaan serupa tentang variabel antarmuka implisit belum lama ini.
Sumber pertanyaan ini adalah bug dalam kode saya karena saya tidak menyadari keberadaan variabel antarmuka implisit yang dibuat oleh kompilator. Variabel ini diselesaikan ketika prosedur yang dimilikinya selesai. Ini pada gilirannya menyebabkan bug karena masa pakai variabel lebih lama dari yang saya perkirakan.
Sekarang, saya memiliki proyek sederhana untuk mengilustrasikan beberapa perilaku menarik dari compiler:
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
dikompilasi seperti yang Anda bayangkan. Variabel lokal I
, hasil fungsi, diteruskan sebagai var
parameter implisit ke Create
. Pembersihan untuk StoreToLocal
hasil dalam satu panggilan ke IntfClear
. Tidak ada kejutan di sana.
Namun, StoreViaPointerToLocal
diperlakukan berbeda. Kompilator membuat variabel lokal implisit yang diteruskannya Create
. Saat Create
kembali, tugas ke P^
dilakukan. Ini meninggalkan rutinitas dengan dua variabel lokal yang memegang referensi ke antarmuka. Pembersihan akan StoreViaPointerToLocal
menghasilkan dua panggilan ke IntfClear
.
Kode yang dikompilasi StoreViaPointerToLocal
adalah seperti ini:
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
Saya bisa menebak mengapa kompilator melakukan ini. Ketika dapat membuktikan bahwa menugaskan ke variabel hasil tidak akan menimbulkan pengecualian (yaitu jika variabel adalah lokal) maka ia menggunakan variabel hasil secara langsung. Jika tidak, ia menggunakan lokal implisit dan menyalin antarmuka setelah fungsi kembali sehingga memastikan bahwa kami tidak membocorkan referensi jika terjadi pengecualian.
Tetapi saya tidak dapat menemukan pernyataan apa pun tentang ini di dokumentasi. Itu penting karena umur antarmuka itu penting dan sebagai programmer Anda harus dapat memengaruhinya sesekali.
Jadi, apakah ada yang tahu jika ada dokumentasi tentang perilaku ini? Jika tidak, apakah ada yang punya pengetahuan lebih tentang itu? Bagaimana bidang contoh ditangani, saya belum memeriksanya. Tentu saja saya dapat mencoba semuanya sendiri, tetapi saya mencari pernyataan yang lebih formal dan selalu lebih suka menghindari mengandalkan detail implementasi yang dikerjakan dengan trial and error.
Perbarui 1
Untuk menjawab pertanyaan Remy, penting bagi saya ketika saya perlu menyelesaikan objek di belakang antarmuka sebelum melakukan penyelesaian lainnya.
begin
AcquirePythonGIL;
try
PyObject := CreatePythonObject;
try
//do stuff with PyObject
finally
Finalize(PyObject);
end;
finally
ReleasePythonGIL;
end;
end;
Seperti yang tertulis seperti ini, tidak apa-apa. Tetapi dalam kode sebenarnya saya memiliki lokal implisit kedua yang diselesaikan setelah GIL dilepaskan dan dibom. Saya memecahkan masalah dengan mengekstraksi kode di dalam Acquire / Release GIL menjadi metode terpisah dan dengan demikian mempersempit ruang lingkup variabel antarmuka.