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”实际上使用多态范式释放并创建了一个实例,但这不是必需的。我只想指出设计应该保持不变。

现在的问题是-为什么会发生以及如何解决?似乎“如果Assigned(o2)”不合适。

我的想法:发送指向my.o2的指针将释放和使o2无效,而我尝试这样做,但是我无法在消息处理程序中从指针转换为对象,不知道为什么。

有人可以帮忙吗?谢谢

最佳答案

您两次释放o2。一次是由于该消息的结果,一次是来自析构函数。

您认为在调用o2时将nil设置为FreeMyObject,但实际上并非如此。您实际上是将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。而且您不需要在消息中传递实例。

我希望您的真实代码不会像这样怪异! ;-)

关于delphi - 为什么会有内存泄漏以及如何解决?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/6974294/

10-11 00:23