2011-08-07 39 views
5
unit Unit7; 

interface 

uses Classes; 

type 
    TListener = class(TThread) 
    procedure Execute; override; 
    end; 

    TMyClass = class 
    o1,o2: Tobject; 
    procedure FreeMyObject(var obj: TObject); 
    constructor Create; 
    destructor Destroy; override; 
    end; 

implementation 

uses Windows, SysUtils; 

var l: TListener; 
    my: TMyClass; 

procedure TListener.Execute; 
var msg:TMsg; 
begin 
    while(GetMessage(msg, Cardinal(-1), 0, 0)) do 
    if(msg.message=6) then begin 
     TMyClass(msg.wParam).FreeMyObject(TObject(msg.lParam)); 
     Exit; 
    end; 
end; 

constructor TMyClass.Create; 
begin 
    inherited; 
    o1:=TObject.Create; 
    o2:=Tobject.Create; // Invalid pointer operation => mem leak 
end; 

destructor TMyClass.Destroy; 
begin 
    if(Assigned(o1)) then o1.Free; 
    if(Assigned(o2)) then o2.Free; 
    inherited; 
end; 

procedure TMyClass.FreeMyObject(var obj: TObject); 
begin 
    FreeAndNil(obj); 
end; 

initialization 
    l:= TListener.Create(); 
    my:=TMyClass.Create; 

    sleep(1000); //make sure the message loop is set 
    PostThreadMessage(l.ThreadID, 6, Integer(my), Integer(my.o2)); 
finalization 
    l.Free; 
    my.Free; 
end. 

我用消息處理程序來說明我的問題,因此您瞭解它。真正的設計要複雜得多。函數'FreeMyObject'實際上釋放並創建一個使用多態主義範例的實例,但這裏不需要。我只想指出,設計應該保持不變。爲什麼有一個mem泄漏,以及如何解決它?

現在的問題和問題 - 爲什麼發生和如何解決它?看起來「如果分配(o2)」不適合它。

我的想法是:發送一個指向my.o2的指針會釋放並且無o2,我試圖這樣做,但是我不能將指針轉換爲消息處理程序中的對象,不知道爲什麼。

有人可以舉手嗎?謝謝

+0

是否'無效指針操作=> MEM leak'確實在該行屬於'O2: = Tobject.Create;' – mjn

+0

@mjn。它可能屬於析構函數中的相應行。 :) – GolezTrol

+0

您最好使用比WM_APP更高的值作爲消息編號。 6是WM_ACTIVATE,可能會造成麻煩。 – GolezTrol

回答

6

您免費o2兩次。一次作爲消息的結果,一次來自析構函數。

當您撥打FreeMyObject時,您認爲您正在設置o2nil但您不是。實際上,您將msg.lParam設置爲0.

o2是一個變量,它包含對對象的引用。您傳遞的值爲o2,當您按值傳遞時,您無法修改您傳遞的值的變量。所以你需要傳遞一個參考o2。要做到這一點,你需要一個指針添加重定向的另一層面傳遞到o2,像這樣:

if(msg.message=6) then begin 
    FreeAndNil(PObject(msg.lParam)^); 
    Exit; 
end; 

... 

PostThreadMessage(l.ThreadID, 6, 0, LPARAM(@my.o2)); 

你不需要FreeMyObject,你可以直接調用FreeAndNil。而且您不需要在消息中傳遞實例。

我希望你的真實代碼不像這樣怪異! ;-)

+1

PostThreadMessage中的推薦強制轉換是PostThreadMessage(l.ThreadID,6,0,LParam(@ my.o2)); –

+0

@LU RD謝謝,你說的很對,我會更新的,我應該知道,因爲我自己的代碼只是說幾個月前! –

+0

是的,已經嘗試過,失敗了。這個問題在別處很多。讓B = class(A)。在這個味精發送的地方lparam = @自我 - 它應該工作。但不會。 @Self不會指向對A的引用所在的地址。可悲的是,我失去了2天,直到找到它。感謝您的努力',接受! – netboy

1

發生了什麼:

程序啓動。初始化運行並向線程發送一條消息,該線程在傳入的引用上調用FreeAndNil。這會將傳入的引用設置爲,但它不會將持有o2的對象字段設置爲。這是一個不同的參考。

然後在析構函數中,由於字段不是,它會嘗試再次釋放它,並且您將得到一個雙免費錯誤(無效的指針操作異常)。由於您在析構函數中引發了異常,因此TMyClass永遠不會被破壞,並且會從內存泄漏。

如果您想這樣做,請將某種類型的標識符傳遞給FreeMyObject而不是參考。如整數2或字符串o2。然後有FreeMyObject使用這個值來查找它應該調用FreeAndNil。 (如果您使用的是Delphi 2010或更高版本,那麼使用RTTI很容易。)這是一個更多的工作,但它會解決您看到的錯誤。

+0

RTTI在頂部,只需要一個指向變量的指針。 –

+0

@David:在你的答案中封裝違規的可能性讓我感到害怕...... P –

+0

這不是真的代碼嗎?如何傳遞一個字符串,因爲它沒有被編組,並且使用rtti類作爲很好的封裝,所以它無法工作?! –

3

如果你想FreeAndNil只發送對象參考Integer(my.o2)的對象是不夠的 - 你需要Integer(@my.o2)。您還應該在代碼中進行相應的更改。

因爲你的代碼是難以調試我寫了一個簡單的演示給予必要的代碼更改一個想法:

type 
    PObject = ^TObject; 

procedure FreeObj(PObj: PObject); 
var 
    Temp: TObject; 

begin 
    Temp:= PObj^; 
    PObj^:= nil; 
    Temp.Free; 
end; 

procedure TForm17.Button1Click(Sender: TObject); 
var 
    Obj: TList; 
    PObj: PObject; 

begin 
    Obj:= TList.Create; 
    PObj:= @Obj; 
    Assert(Obj <> nil); 
    FreeObj(PObj); 
    Assert(Obj = nil); 
end; 
相關問題