2011-09-06 18 views
2

在Delphi中使用Interfaces並重寫引用計數時,可能會繞過Delphi在接口達到引用計數爲零時所做的調用調用。如何通過避免_Release被調用來混合接口和類?

但是 - 當混合類和接口(這是非常有用的)的_Release方法總是叫不管。問題是在下面的示例代碼中,本地對象是空的,但_Release仍然被調用 - 除非在無效內存中。根據應用程序中的內存操作,如果在已刪除的localObject的舊位置上調用_Release,或者如果未重用內存,則可能會導致異常。

所以,可以編譯器生成的呼叫到_Release是「刪除/阻止/避免/殺死/重定向/ VMT劫持/終止/砸到/等等等等等等」?如果可以實現這一點,則在Delphi中有適當的純接口。

unit TestInterfaces; 

interface 

uses 
    Classes, 
    SysUtils; 

type 

    ITestInterface = interface 
    ['{92D4D6E4-A67F-4DB4-96A9-9E1C40825F9C}'] 
    procedure Run; 
    end; 

    TTestClass = class(TInterfacedObject, ITestInterface) 
    protected 
    function _AddRef: Integer; stdcall; 
    function _Release: Integer; stdcall; 
    public 
    procedure Run; 
    end; 

    TRunTestClass = class(TObject) 
    protected 
    FlocalInterface : ITestInterface; 
    FlocalObject : TTestClass; 
    public 
    constructor Create; 
    destructor Destroy; override; 
    procedure Test; 
    end; 

    procedure RunTest; 
    procedure RunTestOnClass; 

var 
    globalInterface : ITestInterface; 

implementation 


procedure RunTest; 
var 
    localInterface : ITestInterface; 
    localObject : TTestClass; 
begin 

    try 

    //create an object 
    localObject := TTestClass.Create; 

    //local scope 
    // causes _Release call when object is nilled 
    localInterface := localObject; 
    localInterface.Run; 

    //or global scope 
    // causes _Release call when exe shuts down - possibly on invalid memory location 
    globalInterface := localObject; 
    globalInterface.Run; 

    finally 
    //localInterface := nil; //--> forces _Release to be called 
    FreeAndNil(localObject); 
    end; 

end; 

procedure RunTestOnClass; 
var 
    FRunTestClass : TRunTestClass; 
begin 
    FRunTestClass := TRunTestClass.Create; 
    FRunTestClass.Test; 
    FRunTestClass.Free; 
end; 


{ TTheClass } 

procedure TTestClass.Run; 
begin 
    beep; 
end; 

function TTestClass._AddRef: Integer; 
begin 
    result := -1; 
end; 

function TTestClass._Release: integer; 
begin 
    result := -1; 
end; 

{ TRunTestClass } 

constructor TRunTestClass.Create; 
begin 
    FlocalObject := TTestClass.Create; 
    FlocalInterface := FlocalObject; 
end; 

destructor TRunTestClass.Destroy; 
begin 
    //.. 
    FlocalObject.Free; 
    //FlocalObject := nil; 
    inherited; 
end; 

procedure TRunTestClass.Test; 
begin 
    FlocalInterface.Run; 
end; 

end. 

回答

3

沒有實際的方法來實現你正在尋找的東西。編譯器會發出呼叫_Release,併爲了重擊他們你需要找到所有的呼叫站點。這不切實際。

恐怕當引用計數的生命週期管理是禁用的唯一可行的辦法是,以確保您完成(即設置爲nil)調用Free之前,你的所有的接口引用。

+0

的確。隨着「整理」,你的意思是「設爲零」,我猜?實際上,不會在接口引用上調用_Release。 –

+0

並且調用_AddRef一次以防止在調用免費之前最後一個接口引用釋放該對象免費 –

+2

這對@Lars不起任何作用。 AddRef函數已經實現,它不會對引用進行計數,因此調用它將不起作用。這是您需要避免的Release調用,並且編譯器將潛在的調用插入到該函數中,而不管當前的引用計數如何。它總是被稱爲非零變量。底線是,即使您沒有明確計算引用,在釋放對象之前,您必須確保接口引用的數量爲零。 –

1

當你使用你不需要任何更多的釋放你的對象的接口。當沒有對同一對象的任何引用時,接口對象將自動釋放。

在您的樣品必須在TTestClass刪除它們在TInterfacedObject類中定義_Release和_AddRef功能。

在runTest方法過程中,您不需要釋放localObject只在最後一節設置globalInterface爲零。程序結束後,localInterface會自動銷燬本地對象。

try 
    ... use your code 
    ... 
finnaly 
    globalInnterface := nil; 
end; 

而關於TTestRun.Destroy剛剛離開這個析構函數空白。你不能釋放FlocalObject。

TTestRun.Destroy; 
begin 
    inherited; 
end; 
+2

這不能解決問題 –

+0

@Mahdi和David說的一樣 – MX4399

相關問題