Warm tip: This article is reproduced from serverfault.com, please click

Clearing Dynamic FMX listview bitmaps from memory

发布于 2020-11-28 23:44:55

I recently started working with dynamic listview items. It's great except for when I try to add and clear items with bitmaps. I have a listview where I add items and download the image into a memstream and assign it to the bitmap of the dynamic listview item. This works except for when I go and clear all the items with lv.items.clear it does not get removed from memory.

The memory just keeps rising even though I clear the old items. Is there a ways to clear all the bitmaps?

Basically what happens is:

  1. Populate dynamic listview with 10 items. Add data and bitmaps.
  2. Look at memory. Went up with 2 megabytes.
  3. Clear the listview with lv.items.clear.
  4. Look at memory. No change?
  5. Repeat and the memory just goes up and up.

I have tried looping through all the listview items and setting each bitmap to nil but no result in any memory change. I also tried freeing each item by looping through it but it just crashes the app.

Should I be clearing all the items or bitmaps in a way? If so how can I go about doing that?

Here's how I'm loading all of the items:

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;

Loading bitmaps from 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;
Questioner
Adriaan
Viewed
0
Remy Lebeau 2020-11-29 08:00:48

Set the 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.