为什么不发布此TStreamAdapter? [英] Why is this TStreamAdapter not released?

查看:70
本文介绍了为什么不发布此TStreamAdapter?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

比较以下两个片段:

(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
);

它按此顺序发生:

  1. TFileStream.Create .
  2. TStreamAdapter.Create .
  3. x._Release 清除旧参考.
  4. 引用新的 IStream .
  1. TFileStream.Create.
  2. TStreamAdapter.Create.
  3. x._Release to clear the old reference.
  4. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆