天天看點

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

為什麼用接口老是會AV錯誤?可能很多人會碰到這樣的問題。

有些人幹脆抛棄用接口來管理對象的生命周期。

我想如果搞清楚接口何時會增加和減少計數應該會掌控到接口何時會銷毀對象進而解決av根本。

做了一個測試

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

procedure TForm1.btnAddInterfaceClick(Sender: TObject);

var

  lvITest: ITest;

begin

  //+1

  lvITest := TTestIntfObject.Create;

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

  lvIntf: ITest;

  lvIntf2: IInterface;

  //正常

  lvIntf2 := FVIList.GetInterfaceByKey('abc');

  lvIntf := (lvIntf2 as ITest);

  lvIntf.showMessage;

  lvIntf := nil;

  lvIntf2 := nil;

  FVIList.Remove('abc');

//還有一種情況,我們可能會經常這樣使用

   //+1 +1  //這樣擷取接口會使FRefCount +2

   lvIntf := (FVIList.GetInterfaceByKey('abc') as ITest);

   lvIntf.showMessage;

   //-1  -這裡隻減1,還有一次會在該函數執行完成時隐含執行

   lvIntf := nil;

   //-1  如果下面代碼對對象進行了手工釋放。就會出現AV錯誤了。

   //當然可以不在這裡進行Remove

   FVIList.Remove('abc');

//

procedure TForm1.btnRemoveClick(Sender: TObject);

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

//下面再看一個過程

procedure TForm1.btnUseInterface2Click(Sender: TObject);

  lvIntf, lvIntf2: ITest;

  lvTest: TTestIntfObject;

  lvVIList:TVIList;

  lvVIList := TVIList.Create(false); //不使用List管理接口對象生命周期(false)

  //+0  //建立對象不增加

  lvTest := TTestIntfObject.Create;

  lvIntf := lvTest;

  lvVIList.Add('abc', lvIntf);

  //+1 +1

  lvIntf2 := (lvVIList.GetInterfaceByKey('abc') as ITest);

  lvIntf2.showMessage;

  //-1 

  lvVIList.Remove('abc');

  lvVIList.Free;

  //這裡進行釋放是會出現錯誤的因為還有一個隐含的接口沒有進行釋放

//procedure TInterfacedObject.BeforeDestruction;

//begin

//  if RefCount <> 0 then

//  begin

      //還存在引用對象的接口沒有釋放!

//    Error(reInvalidPtr);

//  end;

//en

  lvTest.Free;

//////

procedure TVIList.Add(pvKey: string; const pvInterface: IInterface);

  //加上const因為内部不能指派接口是以沒有必要+1

  //如果參數pvInterface沒有const //+1

  if FVIObject.O[pvKey] <> nil then raise Exception.CreateFmt('%s已經注冊接口', [pvKey]);

  //+0

  FVIObject.I[pvKey] := Integer(pvInterface);

  pvInterface._AddRef; //引用

  //如果參數pvInterface沒有const //-1

繼續閱讀