如何在delphi 7中释放stringlist中的对象?

时间:2012-02-05 10:56:56

标签: delphi

以下是Zarko Gajic在Delphi的TStrings项目中释放物体的想法 在About.com.delphi我正在使用Delphi 7,TStringList没有OwnsObjects。

运行以下代码将提示EaccessViolation错误。我不知道为什么和怎么做 走动它来释放物品。

非常感谢。

procedure TForm6.freelist(const alist: TStringList);
var
  i: integer;
begin
  try
    for i:=0 to pred(alist.Count) do begin
      if Assigned(alist.Objects[i]) then begin
        alist.Objects[i].Free;       
        alist.objects[i]:=nil;       
      end;                           
    end;
    alist.Clear;
  finally
    alist.Free;
  end;
end;

修改

我添加了这一行, alist.Objects [i]:=指针(0); 并且没有错误。

...

for i:=0 to pred(alist.Count) do begin
          if Assigned(alist.Objects[i]) then begin
            alist.Objects[i]:=Pointer(0);  //newly added line. 
            alist.Objects[i].Free;       
            alist.objects[i]:=nil;       
          end;                           
        end;
...

//But I do not know if this is correct and 
// if the efficiency will be compromised.
//This is awkward method?

2 个答案:

答案 0 :(得分:5)

以下原始答案回答了您提出的问题。但是,在注释中会发现您没有将对象添加到字符串列表中。您只需添加强制转换为Pointer的整数。在这种情况下,您不得在这些整数上调用Free,这将完全解释您的错误。因此,您的问题中的整个代码都是无偿的,您只需在完成后就可以在列表中调用Free

将整数转换为指针并将其添加到列表时,除了用于存储指针的内存之外,没有分配内存。这是因为整数是类型。当您添加真实指针或对象时,您正在添加reference类型,处理该对象涉及为对象调用Free或为FreeMem(或Dispose)调用指针。

问题的原始答案

您的原始代码是正确的,虽然有点笨重。您遇到的问题是填充Objects[]的代码。由于我们无法看到代码,我们无法说出你的错误。

现在,说过你的代码很笨重,我就是这样写的:

procedure ClearList(List: TStringList);
var
  i: Integer;
begin
  for i := 0 to pred(List.Count) do
    List.Objects[i].Free;       
  List.Clear;
end;

关于上述的一些注释:

  • 在致电if Assigned(obj)之前,您不需要obj.Free测试。我在这里解释原因:Why should I not use "if Assigned()" before using or freeing things?
  • 如果您要致电nil,将项目设置为Clear则没有意义。
  • 你不应该在这样的例程中调用List.Free。列表的生命周期应与清除列表的代码分开管理。对List.Free的调用应在与构造列表的调用相同的范围内进行。或者,如果此列表是类的字段,则应该从拥有类的析构函数调用List.Free

现在,正如我在对此问题和您之前的问题的评论中已经说过的那样,所有这些使用Delphi 7字符串列表的Objects[]属性的生命周期管理工作都非常不令人满意。泄漏物体太容易了。我会推荐以下替代方案:

  1. 切换为使用TObjectList而不是TStringList。创建列表时,将OwnsObjects属性设置为True。这将确保当从列表中删除项目时,它将在删除时被销毁。您只需将列表负责其项目的终身管理。请注意,这将要求您将当前字符串列表代码中存储的string移动为对象的属性。但无论如何,这总是正确的方法,所以我认为这是一个好处而不是缺点。
  2. 如果您希望继续使用字符串列表,请创建自己的处理所有权的派生类,可能是通过添加名为OwnsObjects的属性。将此问题推送到列表中,让您的更高级代码免受该问题的影响。在列表类的上下文中调试一次代码,然后在知道它工作的情况下一遍又一遍地重复使用它。

答案 1 :(得分:-1)

只是一个提示:也许使用TStringList只是使用.indexOf(MyString)来查找项目。

在这种情况下,将代码移动到TObjectList是一种疯狂,你必须重新发明轮子,更好地说,列表上的外观。

我的建议是,最好避免将对象指针设置为非对象的对象,将cat从Integer放到Pointer中非常容易,但最好还是创建{{1}保持这样的整数(不是在速度和内存方面,只是在不破坏定义方面)。

我必须解释一下:如果你存储一个Integer(不是一个对象)应该是一个Object,你将来很容易得到指针错误操作,当你错误地忘记指针没有指向任何东西时。

哦,还有另一件事你只是通过施法而放松:知道它有一个有效的价值;这是因为Integer(Nil)等于零值,因此您无法知道是否将Nil值或零值强制转换为对象指针。

如果你想要它非常简化,也就是没有属性,得到ans set方法等,这里是一个例子:

TIntegerObject

将整数存储为对象是一个非常基本的类,不需要它有一个析构函数,因为在其中没有任何东西需要被释放。

然后,当您想要将整数存储到TStringList的对象上时,您只需执行以下操作:

TIntegerObject=class(TObject)
public
  value:Integer;
  constructor Create(value:Integer);
end;

TIntegerObject.Create(value:Integer);
begin
     inherited Create;
     Self.value:=value;
end;

您可以使用以下命令获取已知字符串值的索引:

MyStringList.Objects[MyIndex]:=TIntegerObject.Create(MyIntegerValue);

然后检查它返回MyIndex:=MyStringList.IndexOf(MyString); ,这意味着这样的字符串不在列表中,它更大意味着字符串在列表的那个位置上。

使用这种方法,您可以检查是否在任何元素上都有一个Integer,只需将其与-1进行比较,例如:

Nil

获取整数值非常简单:

if Nil=MyStringList.Objects[MyIndex]
then begin
     end
else begin
     end;

希望这有助于人们更好地编码。

我知道将它编码为MyInteger:=TIntegerObject(MyStringList.Objects[MyIndex]).value; MyStringList.Objects[MyIndex]:=TObject(MyInteger);要容易得多,但这就像做一个FAKE / LIE,这样做就是告诉MyInteger有一个对象的内存起始位置那不存在;当使用它时,容易导致很多错误,内存指针,这样的内存地址可能不属于app,这样可能会导致错误或者最糟糕的事情,代码注入安全漏洞等等,顺便说一下,这是众所周知的已被用于破解游戏机证券,jailbreacks等。

当然,为每个Integer创建一个对象是浪费时间和内存。

由编码人员决定,使用一种或另一种(或其他)方法,但记得要非常精确(现在和未来),我的建议是如果你将整数作为对象(或指针)添加一个评论,永远不要试图释放它们。

另一个警告:将某些对象的某些属性设置为Nil会导致Free,如果有这样的指针'指向不是对象或应用程序分配的内存的内容,将导致错误。

只是最后一个例子,设置一个TObjectList(MyInteger:=Integer(MyStringList.Objects[MyIndex]);属性设置为OwnsObjects)将对每个元素以及自己的列表中的Free执行Free(至少在我测试的内容上)这样做任何这些代码都可以释放对象和列表:

请注意,此示例True的{​​{1}}属性设置为MyObjectList

OwnsObjects

你必须决定使用什么,我只想更安全,我不知道未来多少年我将需要编辑代码。