以下代码将在关闭OwnerData TListView时报告内存泄漏:
procedure TForm1.Button1Click(Sender: TObject);
begin
ReportMemoryLeaksOnShutdown := True;
ListView1.Items.Add.Caption := 'blah';
end;
我从来没有意识到在OwnerData模式下禁止执行常规Items.Add
?
更糟糕的是,一旦添加了内存,内存泄漏是不可避免的吗?
手动Free
(适用于Item[0]
和Items
)会导致崩溃。
后续的ListView1.OwnerData := False
无法求解。
1条答案
按热度按时间kq4fsx7k1#
我从来没有意识到在
OwnerData
模式下禁止执行常规Items.Add
?它本身并不是严格禁止的,但它会“默默地失败”,不会向您报告任何错误。
TListView.Items.Add()
* 无条件地 * 创建一个新的TListItem
对象,然后将其传递给Win32ListView_InsertItem()
API,作为插入LVITEM
项的LPARAM
。然而,在OwnerData=True
模式下插入将失败,不幸的是TListView
没有检查该条件,因此它没有释放TListItem
,因此泄漏。实际上我会认为这是
TListView
中的一个bug。当不允许Items.Add()
(和其他相关方法)时,我会期望raise
是一个异常。我猜不是。在VCL文档中只有这个警告:https://docwiki.embarcadero.com/Libraries/Alexandria/en/Vcl.ComCtrls.TCustomListView.Items
警告:如果列表视图为虚拟模式,则
Items
为只读,详情请参见OwnerData
。https://docwiki.embarcadero.com/Libraries/en/Vcl.ComCtrls.TCustomListView.OwnerData
...但是,**您必须使用
OnData
、OnDataFind
、OnDataHint
和OnDataStateChange
事件处理程序来管理虚拟列表视图的项目。**例如,如果要显示复选框,则必须显式为列表项目的StateIndex
属性提供值。创建虚拟列表视图时,必须将
Items
的Count
属性设置为虚拟列表中的项目数。这意味着,在使用
OwnerData=True
时,您根本不应该使用Items.Add()
(和其他方法)。您应该将Items.Count
属性设置为您想要显示的项目的 * 数量 *,然后在ListView要求您提供数据时,使用OnData...
事件为每个项目提供数据。更糟糕的是,一旦添加了内存,内存泄漏是不可避免的吗?
是的,因为
TListItem
永远不会被VCL释放,所以您必须手动释放它。手动释放(对于项目[0]和项目)导致崩溃。
正确,因为泄漏的
TListItem
实际上并不在Items
列表中。指向泄漏的TListItem
的 * 唯一 * 有效指针是Items.Add()
返回给您的指针。后续的
ListView1.OwnerData := False
未解决。为什么要这样做呢?ListView在那时并不知道任何关于泄露的
TListItem
的信息。**更新:**我现在已经向Embarcadero提交了一份关于此问题的bug报告:
RSP-41256: Memory leak in TListView when OwnerData=True and Items.Add() is called