J'ai demandé à une semblable question au sujet implicite de l'interface variables n'y a pas longtemps.
La source de cette question a eu un bug dans mon code à moi de ne pas être au courant de l'existence d'un accord implicite de l'interface variable créé par le compilateur. Cette variable a été finalisé lors de la procédure dont il était propriétaire fini. Ce à son tour, a provoqué un bug dû à la durée de vie de la variable étant plus long que ce que j'avais prévu.
Maintenant, j'ai un projet simple pour illustrer certains intéressant de comportement du compilateur:
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
est compilé comme vous pouvez l'imaginer. La variable locale I
, le résultat de la fonction, est passé comme une fonction implicite var
paramètre Create
. Le ranger pour l' StoreToLocal
résultats en un seul appel à l' IntfClear
. Pas de surprise là.
Toutefois, StoreViaPointerToLocal
est traité différemment. Le compilateur crée un implicite des variables locales qui il transmet à la Create
. Lors de l' Create
rendements, la cession P^
est effectuée. Cela laisse la routine avec les deux variables locales détenant des références de l'interface. Le ranger pour l' StoreViaPointerToLocal
résultats dans les deux appels à la IntfClear
.
Le code compilé pour StoreViaPointerToLocal
est comme ceci:
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
Je peux deviner pourquoi le compilateur est en train de faire cela. Lorsqu'il peut prouver que l'affectation à la variable résultat ne sera pas soulever une exception (par exemple, si la variable est locale), alors il utilise la variable résultat directement. Sinon, il utilise un local implicite, une copie de l'interface une fois que la fonction a retourné ainsi que nous devons nous assurer qu'ils ne fuient pas la référence, dans le cas d'une exception.
Mais je ne trouve aucune déclaration de ce dans la documentation. C'est important parce que l'interface durée de vie est importante et en tant que programmeur, vous devez être en mesure de l'influencer à l'occasion.
Donc, est-ce que quelqu'un sait si il existe une documentation de ce comportement? Sinon quelqu'un aurait-il plus de connaissances? Comment sont les champs d'instance traitée, je n'ai pas vérifié encore. Bien sûr, je pourrais essayer de tout pour moi, mais je suis à la recherche d'un cadre plus formel de la déclaration et préfère toujours éviter de compter sur la mise en œuvre détail travaillé par essai et erreur.
Mise à jour 1
Pour répondre à Remy question est compté pour moi quand j'en avais besoin pour finaliser l'objet derrière l'interface avant d'effectuer une autre mise au point.
begin
AcquirePythonGIL;
try
PyObject := CreatePythonObject;
try
//do stuff with PyObject
finally
Finalize(PyObject);
end;
finally
ReleasePythonGIL;
end;
end;
Comme l'écrit comme cela, il est très bien. Mais dans le vrai code, j'ai eu un deuxième local implicite qui a été finalisé après la GIL a été libéré et qui a bombardé. J'ai résolu le problème en extrayant le code à l'intérieur de l'Acquisition/diffusion GIL dans une méthode distincte et donc réduit la portée de l'interface variable.