ฉันถามคำถามที่คล้ายกันเกี่ยวกับตัวแปรอินเทอร์เฟซโดยนัยเมื่อไม่นานมานี้
ที่มาของคำถามนี้คือข้อบกพร่องในโค้ดของฉันเนื่องจากฉันไม่ทราบถึงการมีอยู่ของตัวแปรอินเทอร์เฟซโดยนัยที่สร้างโดยคอมไพเลอร์ ตัวแปรนี้ได้รับการสรุปเมื่อขั้นตอนที่เป็นเจ้าของเสร็จสิ้น สิ่งนี้ทำให้เกิดข้อผิดพลาดเนื่องจากอายุการใช้งานของตัวแปรนานกว่าที่ฉันคาดไว้
ตอนนี้ฉันมีโครงการง่ายๆที่จะแสดงพฤติกรรมที่น่าสนใจบางอย่างจากคอมไพเลอร์:
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 ออกเป็นวิธีการแยกต่างหากและทำให้ขอบเขตของตัวแปรอินเทอร์เฟซแคบลง