注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

BCB-DG's Blog

...

 
 
 

日志

 
 

TInterfacedObject派生类过早释放问题  

2013-12-27 09:19:01|  分类: Delphi |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
//积木
TInterfacedObject是一个实现了IInterface接口的,具有自释放功能的基础类,在应用TInterfacedObject的派生类的时候,有时会在我们不希望它释放的时候,自己就释放了,然后造成程序的空指针错误。
下面我就来解决这个过早释放的问题,并阐述出现问题的原因。

首先用一段代码再现一下TInterfacedObject过早释放的问题

unit uIntfDemo;
interface
uses
  classes,sysutils, Dialogs;
type
  IHello = interface
    ['{356B5F25-6F38-4ACB-8BD9-45FBB7477D38}']
    procedure sayHello();
  end;

  THello = class(TInterfacedObject, IHello)
  public
    procedure sayHello;
  end;

  THelloFactory = class
  private
    fHello : THello;
  public
    constructor Create; virtual;
    destructor Destroy; override;
    function getHello : IHello;
  end;
implementation

{ THello }
procedure THello.sayHello;
begin
  ShowMessage('hello');
end;

{ THelloFactory }
constructor THelloFactory.Create;
begin
  fHello := THello.Create;
end;

destructor THelloFactory.Destroy;
begin
  freeandnil(fHello);
  inherited;
end;

function THelloFactory.getHello: IHello;
begin
  Result := fHello;
end;
end.

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs,StdCtrls,uIntfDemo;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    fHeloloFactory : THelloFactory;
  public
    constructor Create(AOwner : TComponent); override;
    destructor Destroy; override;
  end;

implementation

constructor TForm1.Create(AOwner: TComponent);
begin
  inherited;
  fHeloloFactory := THelloFactory.Create;
end;

destructor TForm1.Destroy;
begin
  freeandnil(fHeloloFactory);
  inherited;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  fHeloloFactory.getHello.sayHello;
end;
end.

在上面的例子中,第一次按下button1按钮可以得到正确的结果,但再次按下button1就会出现空指针异常,如果你有兴趣可以通过在THelloFactory.Destroy里设置断点的办法来观察,THelloFactory实例确实是在第一次按钮按下后给释放掉了。
出现问题的原因在于初次创建THelloFactory之后,引用计数器为0,调用调用THelloFactory.getHello之后,引用计数成了1,在执行完下面的代码后
procedure TForm1.Button1Click(Sender: TObject);
begin
  fHeloloFactory.getHello.sayHello;
end;
THelloFactory实例的引用计数又变成了0,并自动调用destroy方法,将THelloFactory实例销毁,这就是为什么会出现空指针异常的原因了。
解决办法有两个
1.修改派生类创建后的应用计数
unit uIntfDemo;
interface
uses
  classes,sysutils, Dialogs;

type
  IHello = interface
    ['{356B5F25-6F38-4ACB-8BD9-45FBB7477D38}']
    procedure sayHello();
  end;

  THello = class(TInterfacedObject, IHello)
  public
    procedure sayHello;
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
  end;

  THelloFactory = class
  private
    fHello : THello;
  public
    constructor Create; virtual;
    destructor Destroy; override;
    function getHello : IHello;
  end;
implementation

{ THello }
procedure THello.AfterConstruction;
begin
  inherited;
  Inc(FRefCount); //类初次创建后,立刻修改引用计数
end;

procedure THello.BeforeDestruction;
begin 
  FRefCount := 0; //在释放类的时候,引用计数清零,避免抛异常
  inherited;
end;

procedure THello.sayHello;
begin
  ShowMessage('hello');
end;

{ THelloFactory }
constructor THelloFactory.Create;
begin
  fHello := THello.Create;
end;

destructor THelloFactory.Destroy;
begin
  freeandnil(fHello);
  inherited;
end;

function THelloFactory.getHello: IHello;
begin
  Result := fHello;
end;
end.
上面的例子,在创建类的时候将引用计数器增加1,在释放类的时候将引用计数器设置为0,这样就避免了因接口的引用计数变为0而造成的提前释放。
2.试用接口类型声明变量,而不要用类类型来声明变量
unit uIntfDemo;
interface
uses
  classes,sysutils, Dialogs;

type
  IHello = interface
    ['{356B5F25-6F38-4ACB-8BD9-45FBB7477D38}']
    procedure sayHello();
  end;

  THello = class(TInterfacedObject, IHello)
  public
    procedure sayHello;
  end;

  THelloFactory = class
  private   
    fHello: IHello; //不要声明成类,这样可以避免初次创建后,引用计数为0的问题   
  public
    constructor Create; virtual;
    destructor Destroy; override;
    function getHello : IHello;
  end;
implementation

{ THello }
procedure THello.sayHello;
begin
  ShowMessage('hello');
end;

{ THelloFactory }
constructor THelloFactory.Create;
begin
  fHello := THello.Create;
end;

destructor THelloFactory.Destroy;
begin
  fHello := nil;
  inherited;
end;

function THelloFactory.getHello: IHello;
begin
  Result := fHello;
end;
end.

通过接口,解决了初次创建类之后,引用计数为0的问题。
第二种做法更简单一些,也更标准。
掌握了这个方法,你会发现其实TInterfacedObject其实也挺好用的,毕竟接口用起来比类好用多了。
  评论这张
 
阅读(629)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017