简体   繁体   English

如何在Delphi中测试对象是否已释放

[英]How to test whether a object is freed in Delphi

The two programs as shown below try to test whether a object is freed, using the techniques described here Bad reference to an object already freed . 如下所示,这两个程序尝试使用此处介绍的技术测试对象是否被释放。 错误引用已经释放的对象

The first program as shown below runs correctly if compiled under Delphi 7, but wrongly if compiled under Delphi XE and upper. 如果在Delphi 7下编译,则如下所示的第一个程序可以正确运行,而在Delphi XE及更高版本下,则编译不正确。 That is to say, it outputs 也就是说,它输出

 D7          DXE 
True        True
True        True 
True        False
True        True
False       True
False       False

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils;

function ValidateObj(Obj: TObject): Pointer;
// see { Virtual method table entries } in System.pas
begin
  Result := Obj;
  if Assigned(Result) then
    try
      if Pointer(PPointer(Obj)^) <> Pointer(Pointer(Cardinal(PPointer(Obj)^) + Cardinal(vmtSelfPtr))^) then
        // object not valid anymore
        Result := nil;
    except
      Result := nil;
    end;
end;

function ValidateObj2(Obj: TObject): Pointer;
type
  PPVmt = ^PVmt;
  PVmt = ^TVmt;
  TVmt = record
    SelfPtr : TClass;
    Other   : array[0..17] of pointer;
  end;
var
  Vmt: PVmt;
begin
  Result := Obj;
  if Assigned(Result) then
    try
      Vmt := PVmt(Obj.ClassType);
      Dec(Vmt);
      if Obj.ClassType <> Vmt.SelfPtr then
        Result := nil;
    except
      Result := nil;
    end;
end;

var
   Obj: TObject;
begin
  Obj := TObject.Create;
  Writeln(BoolToStr(Assigned(Obj), True));
  Writeln(BoolToStr(Assigned(ValidateObj(Obj)), True));
  Writeln(BoolToStr(Assigned(ValidateObj2(Obj)), True));
  Obj.free;
  Writeln(BoolToStr(Assigned(Obj), True));
  Writeln(BoolToStr(Assigned(ValidateObj(Obj)), True));
  Writeln(BoolToStr(Assigned(ValidateObj2(Obj)), True));
  Readln;
end.

The second program, explicitly using FastMM4, as shown below runs wrongly when compiled under Delphi 7 or XE and upper. 在Delphi 7或XE及更高版本下编译时,第二个程序显式使用FastMM4,如下所示运行错误。 That is to say, it outputs 也就是说,它输出

Expected      D7    DXE
  False     False  False
  True      True   True
  True      True   True 
  True      True   False
  True      False  False
  True      True   True
  False     True   True
  False     True   False

program Project2;

{$APPTYPE CONSOLE}

uses
  FastMM4,
  SysUtils;

function ValidateObj(Obj: TObject): Pointer;
// see { Virtual method table entries } in System.pas
begin
  Result := Obj;
  if Assigned(Result) then
    try
      if Pointer(PPointer(Obj)^) <> Pointer(Pointer(Cardinal(PPointer(Obj)^) + Cardinal(vmtSelfPtr))^) then
        // object not valid anymore
        Result := nil;
    except
      Result := nil;
    end;
end;

function ValidateObj2(Obj: TObject): Pointer;
type
  PPVmt = ^PVmt;
  PVmt = ^TVmt;
  TVmt = record
    SelfPtr : TClass;
    Other   : array[0..17] of pointer;
  end;
var
  Vmt: PVmt;
begin
  Result := Obj;
  if Assigned(Result) then
    try
      Vmt := PVmt(Obj.ClassType);
      Dec(Vmt);
      if Obj.ClassType <> Vmt.SelfPtr then
        Result := nil;
    except
      Result := nil;
    end;
end;

var
   Obj: TObject;
begin
  Obj := TObject.Create;        
  Writeln(BoolToStr(Obj is FastMM4.TFreedObject, True));
  Writeln(BoolToStr(Assigned(Obj), True));
  Writeln(BoolToStr(Assigned(ValidateObj(Obj)), True));
  Writeln(BoolToStr(Assigned(ValidateObj2(Obj)), True));
  Obj.free;                                
  Writeln(BoolToStr(Obj is FastMM4.TFreedObject, True));
  Writeln(BoolToStr(Assigned(Obj), True));
  Writeln(BoolToStr(Assigned(ValidateObj(Obj)), True));
  Writeln(BoolToStr(Assigned(ValidateObj2(Obj)), True));
  Readln;
end.

I am confused how the wrong behavior is caused, and wonder how to test whether a object is freed for Delphi 7 and Delphi XE and upper, especially when FastMM4 is used ? 我很困惑如何导致错误的行为,并且想知道如何测试是否为Delphi 7和Delphi XE及更高版本释放了对象,尤其是当使用FastMM4时?

In general, it is not possible to make a robust test that a pointer refers to an instance that has been freed or not. 通常,不可能对指针指向已释放或未释放的实例进行可靠的测试。 It is the job of the programmer to maintain control of the lifetime of your objects. 程序员的任务是保持对对象生命周期的控制。

There is no way to check whether the object is valid, but to compare its pointer to NIL. 无法检查对象是否有效,只能将其指针与NIL进行比较。 Disallow objects to have more than one pointer, otherwise if this object freed by one pointer, reference to the same object on the second pointer will cause Access Violation. 禁止对象有一个以上指针,否则,如果该对象由一个指针,参照在第二指针相同的对象释放将导致访问冲突。

you can test a VCL object if it is freed/freeing or not with the following code: 您可以使用以下代码测试VCL对象是否已释放/释放:

if  (csFreeNotification in Self.ComponentState) 
or (csDestroying in Self.ComponentState)  then ... //Self is Freed or Freeing.

But you can not apply this method to ordinary pointers (non-VCL objects) 但是您不能将此方法应用于普通指针(非VCL对象)

I had problems with this too, but I got around it by doing the following 我也有这个问题,但我通过以下操作解决了这个问题

First create a new variable directly under interface 首先直接在界面下创建一个新变量

unit Login_Sys;

interface
var
bisnotinmemory:boolean=true;

Then go to the class you would like to randomly check to see if its still in memory's constructor and destructor methods and do something like this 然后转到您要随机检查的类,以查看其是否仍在内存的构造函数和析构函数方法中,并执行类似的操作

constructor TUserlogin.create;
begin
  bisnotinmemory:=False;

and

destructor TUserlogin.free;
begin
  bisnotinmemory:=true;

If you have to keep track of multiple object then you could always make the "bisnotinmemory" variable I used into an array. 如果必须跟踪多个对象,则可以始终将我使用的“ bisnotinmemory”变量放入数组中。

unit Login_Sys;

interface
var
bisnotinmemory: array[0..1] of Boolean = (true, true);

Just remember to add something like "iOBjectID : integer" to the create methode of the class like say 只要记住要在类的create方法中添加“ iOBjectID:integer”之类的内容,例如:

constructor TUserlogin.create(iOBjectID : integer);
begin
  bisnotinmemory[iOBjectID]:=false;
  iPersonalID:=iOBjectID;

You could even declare a variable like "iPersonalID" under the "private" area of the object for use when the destructor method gets called. 您甚至可以在对象的“私有”区域下声明一个变量,例如“ iPersonalID”,以便在调用析构函数方法时使用。

destructor TUserlogin.free;
begin
  bisnotinmemory[iPersonalID]:=true;

I tested this with Delphi 2010 我用Delphi 2010测试了

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何确定 Delphi 对象是否属于特定类而不是任何后代类? - How can I determine whether a Delphi object is of a specific class and not any descendant class? 销毁对象后,Delphi对象字段中的动态数组会自动释放/释放吗? - Will dynamic arrays in fields of Delphi objects be automatically deallocated/freed when the object is destroyed? 在Delphi中使用线程队列时,如何确保释放线程? - How do you ensure that a thread is freed when using a threaded queue in Delphi? 使用Delphi检查HTML代码中是否有<input>对象属性值 - Checking whether there are <input> object attribute values in the HTML Code using Delphi Delphi-无法释放结构的字符串[FastMM管理器] - Delphi - structures' strings not being freed [FastMM manager] 在Delphi线程中,何时释放内存? - In a Delphi thread's, when the memory is freed? Delphi-是否需要释放Treeview数据对象? - Delphi - Does Treeview Data Objects need to be freed? 可以强制Delphi threadvar内存被释放吗? - Possible to force Delphi threadvar Memory to be Freed? 如何测试未知的Delphi RTTI TValue是否反映了任何类型的通用TList <>(或至少TEnumerable <>)的对象? - How can I test if an unknown Delphi RTTI TValue reflects an object that is ANY type of generic TList<> (or at least TEnumerable<>)? 对已释放的对象的错误引用 - Bad reference to an object already freed
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM