2011-08-07 37 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. 

Tôi đã sử dụng trình xử lý tin nhắn để minh họa cho vấn đề của mình như vậy là bạn hiểu nó. Thiết kế thực sự phức tạp hơn rất nhiều. Chức năng 'FreeMyObject' thực sự giải phóng và tạo ra một thể hiện bằng cách sử dụng mô hình đa hình, nhưng điều này ở đây là không cần thiết. Tôi chỉ muốn chỉ ra rằng thiết kế nên giữ nguyên.Tại sao có rò rỉ mem và cách khắc phục?

Bây giờ câu hỏi và vấn đề - tại sao nó xảy ra và cách khắc phục? Có vẻ như 'nếu được gán (o2)' không phù hợp với nó.

Điều tôi nghĩ: Gửi con trỏ tới my.o2 sẽ miễn phí và không o2 và tôi cố gắng làm như vậy, nhưng tôi không thể chuyển đổi từ con trỏ sang đối tượng trong trình xử lý thư, không biết tại sao.

Ai đó có thể trao tay không? Cảm ơn

+0

Liệu 'hoạt động con trỏ không hợp lệ => mem leak' thực sự thuộc về dòng 'o2: = Tobject.Create; ' – mjn

+0

@mjn. Nó có thể thuộc về dòng tương ứng trong destructor. :) – GolezTrol

+0

Bạn nên sử dụng giá trị cao hơn WM_APP cho số thư. 6 là WM_ACTIVATE và có thể gây ra sự cố. – GolezTrol

Trả lời

6

Bạn miễn phí o2 hai lần. Một lần như là kết quả của tin nhắn và một lần từ destructor.

Bạn cho rằng bạn đang đặt o2 thành nil khi bạn gọi FreeMyObject nhưng bạn thì không. Bạn đang thực tế đặt msg.lParam thành 0.

o2 là biến chứa tham chiếu đến đối tượng. Bạn đang chuyển giá trị của o2 và khi bạn chuyển giá trị, bạn không thể sửa đổi biến có giá trị bạn đã vượt qua. Vì vậy, bạn cần chuyển một tham chiếu đến o2. Để làm như vậy bạn cần phải thêm một cấp thêm chuyển hướng và vượt qua một con trỏ đến o2, như vậy:

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

... 

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

Bạn không cần FreeMyObject, bạn chỉ có thể gọi trực tiếp FreeAndNil. Và bạn không cần phải chuyển một thể hiện trong tin nhắn.

Tôi hy vọng mã thực sự của bạn không hoàn toàn lạ như thế này! ;-)

+1

Các diễn viên được đề nghị trong PostThreadMessage đó là ngược và bằng chứng trong tương lai (64 bit) là PostThreadMessage (l.ThreadID, 6.0, LParam (@ my.o2)); –

+0

@LU RD Cảm ơn bạn, bạn hoàn toàn đúng, tôi sẽ cập nhật điều đó, tôi nên biết rằng kể từ khi mã của tôi chỉ nói rằng như của một vài tháng trước đây !! –

+0

Đúng, đã thử điều này và không thành công. Vấn đề là ở nhiều nơi khác. Gọi B = class (A). Trong A, thông điệp này được gửi đến nơi lparam = @ Self - nó sẽ hoạt động. Nhưng sẽ không. @Self sẽ không trỏ đến địa chỉ mà tham chiếu đến A được đặt. Đáng buồn thay, tôi mất 2 ngày cho đến khi tôi tìm thấy nó. Cảm ơn vì nỗ lực tho ', được chấp nhận! – netboy

1

Đây là những gì đang diễn ra:

Bắt đầu chương trình. Khởi tạo chạy và gửi một tin nhắn đến chủ đề, được gọi là FreeAndNil trên tham chiếu được chuyển vào. Điều này đặt tham chiếu được chuyển đến nil, nhưng nó không đặt trường đối tượng đang giữ o2 thành nil. Đó là một tham chiếu khác. Sau đó, trong trường hủy, vì trường không phải là nil, nó cố gắng giải phóng nó một lần nữa và bạn nhận được một lỗi kép miễn phí (ngoại lệ hoạt động con trỏ không hợp lệ). Kể từ khi bạn đưa ra một ngoại lệ trong destructor, TMyClass không bao giờ bị phá hủy và bạn nhận được một rò rỉ bộ nhớ từ nó.

Nếu bạn muốn làm điều này đúng, hãy chuyển một số nhận dạng của một loại nào đó đến FreeMyObject thay vì tham chiếu. Giống như một số nguyên 2 hoặc một chuỗi o2. Sau đó, hãy FreeMyObject sử dụng giá trị này để tra cứu những gì cần gọi FreeAndNil. (Nếu bạn có Delphi 2010 hoặc mới hơn, đó là khá dễ dàng để làm với RTTI.) Đó là một công việc nhiều hơn một chút, nhưng nó sẽ sửa chữa các lỗi bạn đang nhìn thấy.

+0

RTTI là trên đầu trang, chỉ cần một con trỏ đến biến. –

+0

@David: Tiềm năng vi phạm đóng gói trong câu trả lời của bạn khiến tôi sợ hãi ...: P –

+0

nó không phải là mã thực? Làm thế nào để đi qua một chuỗi, mà không làm việc anyway vì nó không nhận được marshalled, và sử dụng lớp rtti như đóng gói tuyệt vời ?! –

3

Nếu bạn muốn FreeAndNil một đối tượng gửi chỉ tham chiếu đối tượng Integer(my.o2) là không đủ - bạn cần Integer(@my.o2). Bạn cũng nên thực hiện các thay đổi tương ứng trong mã của mình.

Kể từ mã của bạn là khó khăn để gỡ lỗi Tôi đã viết một bản demo đơn giản để cung cấp cho một ý tưởng về những thay đổi mã cần thiết:

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; 
Các vấn đề liên quan