zoukankan      html  css  js  c++  java
  • 用接口管理对象的生命周期.

    为什么用接口老是会AV错误?可能很多人会碰到这样的问题。

    有些人干脆抛弃用接口来管理对象的生命周期。

    我想如果搞清楚接口何时会增加和减少计数应该会掌控到接口何时会销毁对象从而解决av根本。

    做了一个测试

    image

    procedure TForm1.btnAddInterfaceClick(Sender: TObject);
    var
      lvITest: ITest;
    begin
      //+1
      lvITest := TTestIntfObject.Create;
      //+1
      FVIList.Add('abc', lvITest);
      //-1
      lvITest := nil;
    end;

    FVIList是自己写的一个接口管理对象,

    *****在这里来讨论下局部变量lvITest,lvITest属于局部变量。即使没有最后一句lvITest :=nil;在该函数运行完成时也会隐含的执行lvItest:=nil;也就是说该对象的FRefCount 还是=1;

    //假如下面代码会释放接口对象.

    FVIList.Remove('abc');

    //下面方法是非常正确的使用接口工作的一种方法

    procedure TForm1.btnUseInterfaceClick(Sender: TObject);
    var
      lvIntf: ITest;
      lvIntf2: IInterface;
    begin
      //正常
      //+1
      lvIntf2 := FVIList.GetInterfaceByKey('abc');
      //+1
      lvIntf := (lvIntf2 as ITest);
      lvIntf.showMessage;
      //-1
      lvIntf := nil;
      //-1
      lvIntf2 := nil;

      //-1
      FVIList.Remove('abc');
    end;

    //还有一种情况,我们可能会经常这样使用

    procedure TForm1.btnUseInterfaceClick(Sender: TObject);
    var
      lvIntf: ITest;
    begin
       //+1 +1  //这样获取接口会使FRefCount +2
       lvIntf := (FVIList.GetInterfaceByKey('abc') as ITest);
       lvIntf.showMessage;
       //-1  -这里只减1,还有一次会在该函数执行完成时隐含执行
       lvIntf := nil;
      

       //-1  如果下面代码对对象进行了手工释放。就会出现AV错误了。

       //当然可以不在这里进行Remove

       FVIList.Remove('abc');
    end;

    //

    procedure TForm1.btnRemoveClick(Sender: TObject);
    begin
      FVIList.Remove('abc');
    end;

    ----------------------------------------------------------------

    //下面再看一个过程

    procedure TForm1.btnUseInterface2Click(Sender: TObject);
    var
      lvIntf, lvIntf2: ITest;
      lvTest: TTestIntfObject;
      lvVIList:TVIList;
    begin
      lvVIList := TVIList.Create(false); //不使用List管理接口对象生命周期(false)

      //+0  //创建对象不增加
      lvTest := TTestIntfObject.Create;

      //+1
      lvIntf := lvTest;

      //+1
      lvVIList.Add('abc', lvIntf);


      //-1
      lvIntf := nil;

      //+1 +1
      lvIntf2 := (lvVIList.GetInterfaceByKey('abc') as ITest);
      lvIntf2.showMessage;
      //-1
      lvIntf2 := nil;

      //-1 
      lvVIList.Remove('abc');

      lvVIList.Free;

      //这里进行释放是会出现错误的因为还有一个隐含的接口没有进行释放

    //procedure TInterfacedObject.BeforeDestruction;
    //begin
    //  if RefCount <> 0 then
    //  begin
          //还存在引用对象的接口没有释放!
    //    Error(reInvalidPtr);
    //  end;
    //en

      lvTest.Free;
    end;

    //////

    procedure TVIList.Add(pvKey: string; const pvInterface: IInterface);
    begin
      //加上const因为内部不能赋值接口所以没有必要+1
      //如果参数pvInterface没有const //+1
      if FVIObject.O[pvKey] <> nil then raise Exception.CreateFmt('%s已经注册接口', [pvKey]);
      //+0
      FVIObject.I[pvKey] := Integer(pvInterface);

      //+1
      pvInterface._AddRef; //引用

      //如果参数pvInterface没有const //-1
    end;

  • 相关阅读:
    oracle中 char,varchar,varchar2的区别
    .net + oracle + win2003部署遇到的问题集合
    Oracle下如何获得随机数,如何保留小数,如何取整数
    Oracle 函数大全(字符串函数,数学函数,日期函数,逻辑运算函数,其他函数)
    Service和广播联合更新UI的例子
    [python] 类常用的内置方法
    android textview 换行 "\r\n"
    java操作excel
    项目指导原则
    SVN与bugzilla整合
  • 原文地址:https://www.cnblogs.com/DKSoft/p/2029290.html
Copyright © 2011-2022 走看看