Tôi đã hỏi một câu hỏi tương tự về các biến giao diện ngầm cách đây không lâu.
Nguồn gốc của câu hỏi này là một lỗi trong mã của tôi do tôi không nhận thức được sự tồn tại của một biến giao diện ngầm được tạo bởi trình biên dịch. Biến này đã được hoàn tất khi thủ tục sở hữu nó hoàn tất. Điều này lại gây ra lỗi do thời gian tồn tại của biến lâu hơn tôi dự đoán.
Bây giờ, tôi có một dự án đơn giản để minh họa một số hành vi thú vị từ trình biên dịch:
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
được biên dịch giống như bạn tưởng tượng. Biến cục bộ I
, kết quả của hàm, được truyền dưới dạng một var
tham số ngầm định cho Create
. Sắp xếp gọn gàng cho StoreToLocal
kết quả trong một cuộc gọi đến IntfClear
. Không có gì ngạc nhiên ở đó.
Tuy nhiên, StoreViaPointerToLocal
được đối xử khác nhau. Trình biên dịch tạo ra một biến cục bộ ngầm mà nó chuyển đến Create
. Khi Create
trả về, việc gán cho P^
được thực hiện. Điều này để lại thói quen với hai biến cục bộ giữ các tham chiếu đến giao diện. Dọn dẹp cho StoreViaPointerToLocal
kết quả trong hai cuộc gọi đến IntfClear
.
Mã đã biên dịch cho StoreViaPointerToLocal
như thế này:
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
Tôi có thể đoán tại sao trình biên dịch lại làm điều này. Khi nó có thể chứng minh rằng việc gán cho biến kết quả sẽ không tạo ra ngoại lệ (tức là nếu biến là cục bộ) thì nó sử dụng trực tiếp biến kết quả. Nếu không, nó sử dụng cục bộ ngầm định và sao chép giao diện khi hàm đã trả về, do đó đảm bảo rằng chúng tôi không làm rò rỉ tham chiếu trong trường hợp có ngoại lệ.
Nhưng tôi không thể tìm thấy bất kỳ tuyên bố nào về điều này trong tài liệu. Nó quan trọng bởi vì thời gian tồn tại của giao diện rất quan trọng và là một lập trình viên, bạn cần phải có khả năng tác động đến nó.
Vì vậy, không ai biết nếu có bất kỳ tài liệu về hành vi này? Nếu không có ai có thêm bất kỳ kiến thức về nó? Các trường mẫu được xử lý như thế nào, tôi vẫn chưa kiểm tra điều đó. Tất nhiên tôi có thể tự mình thử tất cả nhưng tôi đang tìm kiếm một tuyên bố chính thức hơn và luôn muốn tránh dựa vào chi tiết triển khai được thực hiện bằng cách thử và sai.
Cập nhật 1
Để trả lời câu hỏi của Remy, điều quan trọng đối với tôi là khi tôi cần hoàn thiện đối tượng đằng sau giao diện trước khi thực hiện một lần hoàn thiện khác.
begin
AcquirePythonGIL;
try
PyObject := CreatePythonObject;
try
//do stuff with PyObject
finally
Finalize(PyObject);
end;
finally
ReleasePythonGIL;
end;
end;
Như được viết như thế này, nó là tốt. Nhưng trong mã thực, tôi có một cục bộ ngầm thứ hai được hoàn thiện sau khi GIL được phát hành và nó bị đánh bom. Tôi đã giải quyết vấn đề bằng cách trích xuất mã bên trong GIL Acquire / Release thành một phương pháp riêng biệt và do đó thu hẹp phạm vi của biến giao diện.