从内存中清除动态 FMX 列表视图位图
Clearing Dynamic FMX listview bitmaps from memory
我最近开始使用动态列表视图项。它很棒,除了当我尝试使用位图添加和清除项目时。我有一个列表视图,我在其中添加项目并将图像下载到内存流中,并将其分配给动态列表视图项目的位图。这有效,除了当我用 lv.items.clear 清除所有项目时,它不会从内存中删除。
即使我清除了旧项目,内存也一直在上升。有没有办法清除所有位图?
基本上发生的事情是:
- 用 10 个项目填充动态列表视图。添加数据和位图。
- 看看内存。增加了 2 兆字节。
- 使用 lv.items.clear 清除列表视图。
- 看看内存。没有变化?
- 重复,内存就会越来越多。
我尝试遍历所有列表视图项并将每个位图设置为 nil
但没有导致任何内存更改。我还尝试通过循环释放每个项目,但它只会使应用程序崩溃。
我应该以某种方式清除所有项目或位图吗?如果可以,我该怎么做呢?
这是我加载所有项目的方式:
procedure TPluginInstaller.Load;
begin
with frmMain.framePluginManager do
begin
TThread.CreateAnonymousThread(
procedure
begin
var restClient := TRESTClient.Create(nil);
var restRequest := TRESTRequest.Create(nil);
var restResponse := TRESTResponse.Create(nil);
try
restRequest.Client := restClient;
restRequest.Response := restResponse;
restClient.BaseURL := BASE_URL;
restClient.UserAgent := APP_USERAGENT;
restRequest.AddParameter('query', fQuery);
restRequest.AddParameter('page', fPage.ToString);
restRequest.AddParameter('sort', SortBy[fSort]);
if fSort = 0 then
restRequest.AddParameter('sortdir', 'asc')
else
restRequest.AddParameter('sortdir', 'desc');
restRequest.AddParameter('categories[]', 'rust');
restRequest.Execute;
var jdata := restResponse.JSONValue;
fLastPage := jdata.GetValue<Integer>('last_page', 1);
fPage := jdata.GetValue<Integer>('current_page', 1);
TThread.Synchronize(nil,
procedure
begin
spnedtPage.Max := fLastPage;
spnedtPage.Value := fPage;
lblPageMax.Text := ' of ' + fLastPage.ToString;
lvPluginInstaller.BeginUpdate;
try
lvPluginInstaller.Items.Clear;
for var jplugins in (jdata.FindValue('data') as TJSONArray) do
begin
var aItem := lvPluginInstaller.Items.Add;
var aIcon := aItem.Objects.FindObjectT<TListItemImage>('Icon');
var aDownloadsIcon := aItem.Objects.FindObjectT<TListItemImage>('DownloadsIcon');
var aVersionIcon := aItem.Objects.FindObjectT<TListItemImage>('VersionIcon');
var aAuthorIcon := aItem.Objects.FindObjectT<TListItemImage>('AuthorIcon');
var aUpdatedIcon := aItem.Objects.FindObjectT<TListItemImage>('UpdatedIcon');
var aTitle := aItem.Objects.FindObjectT<TListItemText>('Title');
var aDescription := aItem.Objects.FindObjectT<TListItemText>('Description');
var aVersion := aItem.Objects.FindObjectT<TListItemText>('Version');
var aDownloads := aItem.Objects.FindObjectT<TListItemText>('Downloads');
var aAuthor := aItem.Objects.FindObjectT<TListItemText>('Author');
var aURL := aItem.Objects.FindObjectT<TListItemText>('URL');
var aUpdated := aItem.Objects.FindObjectT<TListItemText>('Updated');
GetIcon(jplugins.GetValue<string>('icon_url').Trim, aIcon);
aDownloadsIcon.ImageIndex := 0;
aVersionIcon.ImageIndex := 1;
aAuthorIcon.ImageIndex := 2;
aUpdatedIcon.ImageIndex := 4;
aTitle.Text := jplugins.GetValue<string>('title', 'Unknown Plugin Title');
aDescription.Text := jplugins.GetValue<string>('description', 'Unknown Plugin Description');
aVersion.Text := jplugins.GetValue<string>('latest_release_version_formatted', 'Unknown Version');
aDownloads.Text := jplugins.GetValue<string>('downloads_shortened', 'Unknown Downloads');
aAuthor.Text := jplugins.GetValue<string>('author', 'Unknown Author');
aURL.Text := jplugins.GetValue<string>('json_url', 'Unknown URL');
aUpdated.Text := jplugins.GetValue<string>('latest_release_at', 'Unknown');
end;
finally
lvPluginInstaller.EndUpdate;
end;
end);
finally
restResponse.Free;
restRequest.Free;
restClient.Free;
end;
end).Start;
end;
end;
正在从 url 加载位图:
procedure TPluginInstaller.GetIcon(const aURL: string; aIcon: TListItemImage);
begin
if aURL = '' then
begin
aIcon.ImageIndex := 3;
Exit;
end;
TThread.CreateAnonymousThread(
procedure
begin
var imgStream := TMemoryStream.Create;
try
TDownloadURL.DownloadRawBytes(aURL, imgStream);
TThread.Synchronize(nil,
procedure
begin
aIcon.Bitmap := TBitmap.CreateFromStream(imgStream);
end);
finally
imgStream.Free;
end;
end).Start;
end;
设置TListItemImage.OwnsBitmap
property to True, otherwise you are responsible for freeing the TBitmap
objects manually when you are done using them. Note that starting with Delphi 10.4, ARC is no longer used for object memory management on mobile platforms:
Unified Memory Management
- Delphi memory management is now unified across all supported platforms - mobile, desktop, and server - using the classic implementation of object memory management. Compared to Automatic Reference Counting (ARC), this offers better compatibility with existing code and simpler coding for components, libraries, and end-user applications. The ARC model remains for string management and interface type references for all platforms.
- For C++, this change means that the creation and deletion of Delphi-style classes in C++ follow normal memory management just like any heap-allocated C++ class, significantly reducing complexity.
我最近开始使用动态列表视图项。它很棒,除了当我尝试使用位图添加和清除项目时。我有一个列表视图,我在其中添加项目并将图像下载到内存流中,并将其分配给动态列表视图项目的位图。这有效,除了当我用 lv.items.clear 清除所有项目时,它不会从内存中删除。
即使我清除了旧项目,内存也一直在上升。有没有办法清除所有位图?
基本上发生的事情是:
- 用 10 个项目填充动态列表视图。添加数据和位图。
- 看看内存。增加了 2 兆字节。
- 使用 lv.items.clear 清除列表视图。
- 看看内存。没有变化?
- 重复,内存就会越来越多。
我尝试遍历所有列表视图项并将每个位图设置为 nil
但没有导致任何内存更改。我还尝试通过循环释放每个项目,但它只会使应用程序崩溃。
我应该以某种方式清除所有项目或位图吗?如果可以,我该怎么做呢?
这是我加载所有项目的方式:
procedure TPluginInstaller.Load;
begin
with frmMain.framePluginManager do
begin
TThread.CreateAnonymousThread(
procedure
begin
var restClient := TRESTClient.Create(nil);
var restRequest := TRESTRequest.Create(nil);
var restResponse := TRESTResponse.Create(nil);
try
restRequest.Client := restClient;
restRequest.Response := restResponse;
restClient.BaseURL := BASE_URL;
restClient.UserAgent := APP_USERAGENT;
restRequest.AddParameter('query', fQuery);
restRequest.AddParameter('page', fPage.ToString);
restRequest.AddParameter('sort', SortBy[fSort]);
if fSort = 0 then
restRequest.AddParameter('sortdir', 'asc')
else
restRequest.AddParameter('sortdir', 'desc');
restRequest.AddParameter('categories[]', 'rust');
restRequest.Execute;
var jdata := restResponse.JSONValue;
fLastPage := jdata.GetValue<Integer>('last_page', 1);
fPage := jdata.GetValue<Integer>('current_page', 1);
TThread.Synchronize(nil,
procedure
begin
spnedtPage.Max := fLastPage;
spnedtPage.Value := fPage;
lblPageMax.Text := ' of ' + fLastPage.ToString;
lvPluginInstaller.BeginUpdate;
try
lvPluginInstaller.Items.Clear;
for var jplugins in (jdata.FindValue('data') as TJSONArray) do
begin
var aItem := lvPluginInstaller.Items.Add;
var aIcon := aItem.Objects.FindObjectT<TListItemImage>('Icon');
var aDownloadsIcon := aItem.Objects.FindObjectT<TListItemImage>('DownloadsIcon');
var aVersionIcon := aItem.Objects.FindObjectT<TListItemImage>('VersionIcon');
var aAuthorIcon := aItem.Objects.FindObjectT<TListItemImage>('AuthorIcon');
var aUpdatedIcon := aItem.Objects.FindObjectT<TListItemImage>('UpdatedIcon');
var aTitle := aItem.Objects.FindObjectT<TListItemText>('Title');
var aDescription := aItem.Objects.FindObjectT<TListItemText>('Description');
var aVersion := aItem.Objects.FindObjectT<TListItemText>('Version');
var aDownloads := aItem.Objects.FindObjectT<TListItemText>('Downloads');
var aAuthor := aItem.Objects.FindObjectT<TListItemText>('Author');
var aURL := aItem.Objects.FindObjectT<TListItemText>('URL');
var aUpdated := aItem.Objects.FindObjectT<TListItemText>('Updated');
GetIcon(jplugins.GetValue<string>('icon_url').Trim, aIcon);
aDownloadsIcon.ImageIndex := 0;
aVersionIcon.ImageIndex := 1;
aAuthorIcon.ImageIndex := 2;
aUpdatedIcon.ImageIndex := 4;
aTitle.Text := jplugins.GetValue<string>('title', 'Unknown Plugin Title');
aDescription.Text := jplugins.GetValue<string>('description', 'Unknown Plugin Description');
aVersion.Text := jplugins.GetValue<string>('latest_release_version_formatted', 'Unknown Version');
aDownloads.Text := jplugins.GetValue<string>('downloads_shortened', 'Unknown Downloads');
aAuthor.Text := jplugins.GetValue<string>('author', 'Unknown Author');
aURL.Text := jplugins.GetValue<string>('json_url', 'Unknown URL');
aUpdated.Text := jplugins.GetValue<string>('latest_release_at', 'Unknown');
end;
finally
lvPluginInstaller.EndUpdate;
end;
end);
finally
restResponse.Free;
restRequest.Free;
restClient.Free;
end;
end).Start;
end;
end;
正在从 url 加载位图:
procedure TPluginInstaller.GetIcon(const aURL: string; aIcon: TListItemImage);
begin
if aURL = '' then
begin
aIcon.ImageIndex := 3;
Exit;
end;
TThread.CreateAnonymousThread(
procedure
begin
var imgStream := TMemoryStream.Create;
try
TDownloadURL.DownloadRawBytes(aURL, imgStream);
TThread.Synchronize(nil,
procedure
begin
aIcon.Bitmap := TBitmap.CreateFromStream(imgStream);
end);
finally
imgStream.Free;
end;
end).Start;
end;
设置TListItemImage.OwnsBitmap
property to True, otherwise you are responsible for freeing the TBitmap
objects manually when you are done using them. Note that starting with Delphi 10.4, ARC is no longer used for object memory management on mobile platforms:
Unified Memory Management
- Delphi memory management is now unified across all supported platforms - mobile, desktop, and server - using the classic implementation of object memory management. Compared to Automatic Reference Counting (ARC), this offers better compatibility with existing code and simpler coding for components, libraries, and end-user applications. The ARC model remains for string management and interface type references for all platforms.
- For C++, this change means that the creation and deletion of Delphi-style classes in C++ follow normal memory management just like any heap-allocated C++ class, significantly reducing complexity.