Delphi-判断一个对象是否释放,改造官方的Assigned

发布时间 2023-05-28 05:49:17作者: 老衲88

直接上例子了,基础知识自己去了解,首先定义一个类:

  TPerson = class
  public
    name: string;
    age: Integer;
    constructor Create(name: string; age: Integer);
  end;
  
constructor TPerson.Create(name: string; age: Integer);
begin
  Self.name := name;
  Self.age := age;
end;

 

例子1,首先了解Free,仅仅是销毁堆中数据,栈中变量的值依然是堆中的地址:

procedure TForm3.btn1Click(Sender: TObject);
var
  ps: TPerson;
begin
  ps := TPerson.Create('小李', 10);

  ps.Free; //这里调用Free,堆中的数据已经被释放,但是ps这个指针的值还是堆中对象的地址

  if Assigned(ps) then //Assigned方法,当ps指针的值为nil的时候,才返回False,由于ps的值不为nil,这里显然会返回True
  begin
    mmo1.Lines.Add('yes');
    mmo1.Lines.Add(ps.name); //这里产生了Av错误,原因是ps堆中的对象已经被释放
  end else begin
    mmo1.Lines.Add('no');
  end;
end;

 

 

例子2,知道即使你不创建实例,Assigned依然是true;

procedure TForm3.btn3Click(Sender: TObject);
var
  p: TPerson;
begin
  //p不初始化,会自由随机分配一个地址和值,值也是原来的自由值,不一定是nil,通常都不是nil
  if Assigned(p) then
  begin
    //只要p指针的值不是nil,这里就会返回true,所以是yes
    mmo1.Lines.Add('yes');
  end else begin
    mmo1.Lines.Add('no');
  end;
end;

 

 

例子3:对例子1进行改造,使用FreeAndNil,将堆中数据释放,同时将栈中指针的值变为nil;

procedure TForm3.btn3Click(Sender: TObject);
var
  ps: TPerson;
begin
  ps := TPerson.Create('小李', 10);

  FreeAndNil(ps); //指针的值也变成nil了

  if Assigned(ps) then
  begin
    mmo1.Lines.Add('yes');
  end else begin
    mmo1.Lines.Add('no'); //此时就自然到这里了
  end;
end;

 

 

例子4,最后一个例子,得出官方的Assigned 不是我们需要的:

 

procedure TForm3.btn4Click(Sender: TObject);
var
  ps, ps1: TPerson;
begin
  ps := TPerson.Create('小李', 10);

  ps1 := ps;

  FreeAndNil(ps);

  if Assigned(ps) then
  begin
    mmo1.Lines.Add('yes');
  end else begin
    mmo1.Lines.Add('no'); //ps指针的值是nil了,所以no
  end;

  if Assigned(ps1) then
  begin
    mmo1.Lines.Add('yes'); //ps1指针的值不是nil
    //mmo1.Lines.Add(ps1.name); //这里就会报错,因为ps堆中的数据已经释放
  end else begin
    mmo1.Lines.Add('no');
  end;

  //显然上面不是我们要的结果,使用我们自己的函数
  if AssignedEx(ps1, TPerson) then
  begin
    mmo1.Lines.Add('yes');
  end else begin
    mmo1.Lines.Add('no'); //由于ps1指向的堆中数据已经不存在,这里会no
  end;
end;

 

好了综上得出官方的Assigned 有点简陋,不是我们需要的,引出我们的修改版:

/// <summary>
/// 判断实例释放释放
/// </summary>
/// <param name="AVar">实例变量</param>
/// <param name="className">实例类型</param>
function AssignedEx(AVar: TObject; AClass: TClass): Boolean;
begin
  //首先判断变量指针的值是否为nil
  if not Assigned(AVar) then
  begin
    Exit(False);
  end;

  //判断堆中的数据是否是当前类
  try
    //若AVar指向的堆中数据已释放,则这里调用.ClassName会AV异常
    if AVar.ClassName = AClass.ClassName then
    begin
      Exit(True);
    end else begin
      Exit(False);
    end;
  except
    //有异常说明堆中已经被释放掉了
    Exit(False);
  end;
end;