【MyBean调试笔记】接口的使用和清理二(关于接口在MyBean中的应用注意)

【DEMO信息】

有问题的DEMO, 由【[北京]火鸟】提交

【问题描述】

程序关闭时出现AV错误

JEGJM3O3N445M38LK{JWTX0

【问题解答】

1:你有一个TDataMoudle的单实例插件,GetBean后他的Owner是vclOwners。在退出程序时清理了VclOwners后,然后清理单实例的接口为nil时出现了AV错误, 因为单实例要保留一份接口, 一般情况是不会出现av的
     这里mybean可以优化下,先清理单实例的接口, 但是也不能保证,如果插件在其他DLL时会出现的情况

2:第二个问题是主窗体上面声明了很多插件的实例接口, 如果不提前Free主窗体, 系统关闭主窗体的时候,Delphi会在清理对象的时候会自动清理接口:   接口:= nil, 这样我们都知道 会触发实例的release方法, 但是这个实例已经被释放了,而那块内存可能被破坏。所以主窗体先释放,避免提前清理接口

     (插件接口如果是由TComponent类已经他的子类实现, 可以在对象的Destroy时手动清理变量Pointer(AComponentIntf) := nil;  这样不会触发release方法。如果是接口管理生命周期的对象请不要这样做)

这也是我越来越讨厌Delphi自动编译add/release方法的原因了, 虽然component的release方法都基本没什么用, 但是如果这个实例对应的内存地址已经被破坏, 再去访问relase对应的那块地址时可能会参数AV

 

出现问题的原理就是我刚刚说的哪两种情况
归根到底还是接口赋值为nil的时候,触发了一块无效的对象空间的relase方法

 

 

【建议使用规范】

1. 窗体除非特殊的情况, 一般不要注册单实例,在关闭后,最好去释放窗体, 比如Action := caFree。

2. 主窗体提前释放,然后清理插件。推荐写法:

image

3. 单实例一般建议用由接口管理对象生命周期插件(继承TInterfacedObject)。而非TCompnent类级子类对象。

【MyBean调试笔记】关于单元的释放顺序

【概述】

DEMO提交人:惠商软件  2508696439

问题描述:MDIConsole, DEMO如果Forms单元引用顺序放在mybean.console.pas文件之后如下图所示时:

image

创建同一EXE内的MDI子插件并显示,在不关闭MDI子窗体的情况下,关闭主EXE时,会造成关闭时产生访问违规错误。

image

【调试过程】

看了问题,能重现,能重新的问题就不算什么问题了,一定可以找到原因的,开启debug dcus后,发现错误停留产生时,停留在Screen.FSaveFocusedXXX这一句。调试发现果然Screen为nil了。

(PEV87O}1LU$O[ESGWQ_R_K

 

【问题分析】

mybean在创建插件TComponent子插件时会传入beanFactory.VclOwners, 这样在程序关闭时,清理类工厂时,会释放他,并清理他的子组件,达到自动清理的插件的目的,因为getBean的插件没有手动关闭,是等待清理beanFactory时进行清理的,清理的时候,Screen已经被清理,所以产生了该错误。

我们看看工程文件的引用顺序

program MDIConsole;

uses
  FastMM4,
  FastMM4Messages,

  mybean.console,
  mybean.core.beanFactory,

  Forms,

上面代码来看是会先清理Forms,然后再清理mybean.console,和mybean.core.beanFactory单元,但是因为mybean.core.beanFactory里面引用了Forms单元,所以清理顺序变成了

1.mybean.core.beanFactory,

2.Forms,

3.mybean.console,

 

mybean.console单元中管理了所由的类工厂插件接口,因为类工厂是通过接口来决定生命周期的,所以在清理mybean.console单元的时候,才会释放最后一个工厂接口的引用,也就是释放vclOwners和他的子插件,这个时候application和screen实例都已经被释放了。所以procedure TCustomForm.BeforeDestruction;中访问Screen才会引发异常。

现在来优化下mybean的核心类。

mybean.core.beanFactory和mybean.console单元中都引用Forms单元,这样不管你顺序怎么调整,都让Forms单元成为在这两个单元后面。然后在mybean.core.beanFactory被清理时,清理工厂接口资源。

使顺序变成这样

1.mybean.core.beanFactory,

2.mybean.console,

3.Forms。

【MyBean调试笔记】接口的使用和清理

【DEMO信息】

有问题的DEMO, 由芒果提交

【问题描述】

双击Tab页面关闭页面和插件的时候出现AV异常

image

【问题调试】

我们在调试的时候出现这个错误时断点停在这里

image

可以看到停留在这个位置@IntfClear,指针清理的位置

image

好我们来调试下这个过程,跟一下是什么时候出现的, 在end 处下个断点后切换到cpu页,我们看到在ret之前调用了一过程,这个地址应该是做一些清理工作后然后返回到调用者地址,

`TDZCGURW[(RGU7B72)5CNY

跟进去后,一会就跳到错误的界面,可以说明在清理时出现的,这个时候我们来看看这段代码

复制代码

procedure TfrmMain.pgcMainMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  Index: Integer;
  lvPluginForm: IPluginForm;
  lvTabShtEx:TTabShtEx;
begin
  // 左键点击并且双击
  if (Button = mbLeft) and (ssDouble in Shift) then
  begin
    Index := pgcMain.IndexOfTabAt(X, Y);
    if Index >= 0 then
    begin
      lvTabShtEx := TTabShtEx(pgcMain.Pages[Index]);
      lvPluginForm := lvTabShtEx.PluginForm;
      if Application.MessageBox('确认要关闭画面吗?', '询问', MB_OKCANCEL + MB_ICONQUESTION) = IDCANCEL then
        Exit;
      lvPluginForm.freeObject;
      pgcMain.Pages[Index].Free;
    end;
  end;
end;

复制代码

这段代码中有一个lvPluginForm为接口IPluginForm变量, 过程在退出时会执行lvPluginForm := nil和其他一些资源的清理工作,清理的时候会触发对象的__release方法,但是我们看到这个对象已经释放掉了,然后在进行清理的时候出现了上面看到的访问违规的错误。

【问题解决】

找到出现问题的根本,解决起来就很快了,尽量不要等到过程清理时在去清理你的资源,特别是可能访问不存在的资源。在end;之前 加一句lvPluginForm := nil;这样就好了

分类: [MyBean说明书]