用接口管理对象的生命周期.

为什么用接口老是会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;

原文地址:https://www.cnblogs.com/DKSoft/p/2029290.html