这是声明IPersistStream.Save
:
function Save(const stm: IStream; fClearDirty: BOOL): HResult; stdcall;
关键点是流参数传递为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 的说法,关于传递给 const 参数的接口的问题是一个错误。它从未被修复,我个人已经放弃了这种情况发生的希望。
我的 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.
Output
Instance1 created
Foo
Instance2 created
Instance2 _AddRef
Bar
Instance2 _Release
Instance2 destroyed