为什么不发布此TStreamAdapter? [英] Why is this TStreamAdapter not released?
问题描述
比较以下两个片段:
(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
调用时它将被销毁.所以我尝试了这个:
This fails on the second TFileStream.Create
because the first isn't destroyed. This is odd since the parameter has the only reference, I thought it would get destroyed in closing up the Save
call. So I tried this:
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的已知问题吗?是因为链接器/编译器开关吗?
Which works fine. (But fails again without x:=nil;
) So don't worry about d
, it is a IPersistStream
and is behaving correctly. Why does it take an explicit nil
assignment to force the _Release
call? Is this a know issue with Delphi 7? Is it because of a linker/compiler switch?
推荐答案
这是 IPersistStream.Save
的声明:
function Save(const stm: IStream; fClearDirty: BOOL): HResult; stdcall;
关键是流参数作为 const
传递.这意味着 Save
函数不会引用 IStream
接口.它的参考计数既不递增也不递减.而且由于两者均未发生,因此它永远不会被破坏.
The key point is that the stream parameter is passed as const
. That means that the Save
function does not take a reference to the IStream
interface. Its reference count is neither incremented or decremented. And since neither happens, it is never destroyed.
解决该问题的方法是确保某些内容保留了对该接口的引用.您在第二个示例中演示了这一点.
The way to work around it is to make sure that something holds a reference to the interface. Which is what you demonstrate in the second example.
您需要为 nil
分配代码的原因取决于该代码的执行顺序:
The reason that you need the assignment to nil
is down to the order in which this code is executed:
x := TStreamAdapter.Create(
TFileStream.Create('test.bin',fmOpenRead),soOwned
);
它按此顺序发生:
-
TFileStream.Create
. -
TStreamAdapter.Create
. -
x._Release
清除旧参考. - 引用新的
IStream
.
TFileStream.Create
.TStreamAdapter.Create
.x._Release
to clear the old reference.- Take a reference to the new
IStream
.
这显然是错误的顺序.您需要先清除 x
,然后再调用 TFileStream.Create
.
And that is clearly in the wrong order. You need to clear x
before calling TFileStream.Create
.
根据前Embarcadero编译器工程师Barry Kelly的说法,
According to former Embarcadero compiler engineer, Barry Kelly, the issue regarding the interface passed to a const parameter is a bug. It has never been fixed and I for one have given up hope of that ever happening.
我的SSCCE来演示此问题:
My SSCCE to demonstrate the issue is here:
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
这篇关于为什么不发布此TStreamAdapter?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!