比较这两个片段:
(d as IPersistStream).Save(
TStreamAdapter.Create(
TFileStream.Create('test.bin',fmCreate),soOwned),true);
(d as IPersistStream).Load(
TStreamAdapter.Create(
TFileStream.Create('test.bin',fmOpenRead),soOwned));
第二个TFileStream.Create
失败,因为第一个没有被销毁。这很奇怪,因为参数有唯一的引用,我认为它会在关闭Save
调用时被破坏。所以我尝试了这个:
var
x:IStream;
begin
x:=TStreamAdapter.Create(
TFileStream.Create('test.bin',fmCreate),soOwned);
(d as IPersistStream).Save(x,true);
x:=nil;
x:=TStreamAdapter.Create(
TFileStream.Create('test.bin',fmOpenRead),soOwned);
(d as IPersistStream).Load(x);
x:=nil;
哪个工作正常。 (但如果没有x:=nil;
则再次失败)所以不要担心d
, 是IPersistStream
并且行为正确。为什么要使用明确的nil
分配来强制_Release
来电?这是Delphi 7的已知问题吗?是因为链接器/编译器切换?
答案 0 :(得分:5)
以下是IPersistStream.Save
的声明:
function Save(const stm: IStream; fClearDirty: BOOL): HResult; stdcall;
关键是stream参数传递为const
。这意味着Save
函数不会引用IStream
接口。它的引用计数既不递增也不递减。既然都没有发生,它永远不会被摧毁。
解决它的方法是确保某些东西包含对接口的引用。这是你在第二个例子中演示的内容。
您需要分配到nil
的原因取决于执行此代码的顺序:
x := TStreamAdapter.Create(
TFileStream.Create('test.bin',fmOpenRead),soOwned
);
按顺序发生:
TFileStream.Create
。TStreamAdapter.Create
。x._Release
清除旧参考。IStream
。这显然是错误的顺序。在致电x
之前,您需要先清除TFileStream.Create
。
根据前Embarcadero编译工程师Barry Kelly的说法,the issue regarding the interface passed to a const parameter is a bug。它从来没有被修复过,我一个人已经放弃了这种情况的希望。
我的SSCCE来证明这个问题在这里:
program SO22846335;
{$APPTYPE CONSOLE}
type
TMyInterfaceObject = class(TObject, IInterface)
FRefCount: Integer;
FName: string;
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
constructor Create(const Name: string);
destructor Destroy; override;
end;
constructor TMyInterfaceObject.Create(const Name: string);
begin
inherited Create;
FName := Name;
Writeln(FName + ' created');
end;
destructor TMyInterfaceObject.Destroy;
begin
Writeln(FName + ' destroyed');
inherited;
end;
function TMyInterfaceObject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
Result := E_NOINTERFACE;
end;
function TMyInterfaceObject._AddRef: Integer;
begin
Writeln(FName + ' _AddRef');
Result := AtomicIncrement(FRefCount);
end;
function TMyInterfaceObject._Release: Integer;
begin
Writeln(FName + ' _Release');
Result := AtomicDecrement(FRefCount);
if Result = 0 then
Destroy;
end;
procedure Foo(const Intf: IInterface);
begin
Writeln('Foo');
end;
procedure Bar(Intf: IInterface);
begin
Writeln('Bar');
end;
begin
Foo(TMyInterfaceObject.Create('Instance1'));
Bar(TMyInterfaceObject.Create('Instance2'));
Readln;
end.
<强>输出强>
Instance1 created Foo Instance2 created Instance2 _AddRef Bar Instance2 _Release Instance2 destroyed