Invalid Pointer Operation on TObjectList.DisposeOf

166 Views Asked by At

Hello folks and sorry if this is a duplicate but my specific issue I haven't seen answered anywhere yet. I have an "Invalid Pointer Operation" when I try to free an ObjectList created at runtime at the following lines:

  Prods := TItemProcedimento.Create(DM.FDQ).lerProdutos;
  Prods.DisposeOf; // <- Invalid Pointer Operation at 2nd iteration

So, here's my classes:

unit uItemProcedimento;

interface
[...]

type
  TItemProcedimento = class
[...]
public
    constructor Create(DataSet: TFDQuery);
    function lerProdutos: TObjectList<TItemProcedimento>;
[...]

constructor TItemProcedimento.Create(DataSet: TFDQuery);
begin
  FDataSet := DataSet;
end;

function TItemProcedimento.lerProdutos: TObjectList<TItemProcedimento>;
begin
  Result := TObjectList<TItemProcedimento>.Create;
  try
    FDataSet.Close;
    FDataSet.SQL.Clear;
    FDataSet.SQL.Add('SELECT *');
    FDataSet.SQL.Add('FROM Produto p');
    FDataSet.SQL.Add('JOIN ItensProcedimento IP on p.PRO_ID = IP.PRO_ID');
    FDataSet.SQL.Add('ORDER BY p.PRO_Nome');
    FDataSet.Open;
    while not FDataSet.Eof do
    begin
      PRO_ID := FDataSet.FieldByName('PRO_ID').AsInteger;
      PRO_Rendimento := FDataSet.FieldByName('PRO_Rendimento').AsInteger;
      PRO_Nome := FDataSet.FieldByName('PRO_Nome').AsString;
      PRO_Tipo := FDataSet.FieldByName('PRO_Tipo').AsInteger;
      PRO_Custo := FDataSet.FieldByName('PRO_Custo').AsFloat;
      PRO_Potencia := FDataSet.FieldByName('PRO_Potencia').AsFloat;
      IPR_Uso := FDataSet.FieldByName('IPR_Uso').AsFloat;
      Result.Add(self);
      FDataSet.Next;
    end;
  finally
    FDataSet.Close;
  end;

The weirdest thing about this is that I have another class with the exact same behaviour and the method works without issues. Yes I am creating the object and immediately destroying it to test if I'm destroying it correctly. In another class I have another example with the same code, but this time it works without errors

 test := TMyClass.Create(DM.FDQ).lerTeste;
 test.DisposeOf;

Why? What am I doing wrong? This test code is running before the current code btw, maybe its related?

UPDATE: By applying the changes that Remy Lebeau suggested I managed to add properly the items to the list and dispose them so this particular part of the code has no leaks. But in another part of the code I have an ObjectList leak that I have no idea on how to fix.

Inside my class I have a property that is a TObjectList property, I have a method that checks if the list is assigned, if not, it creates it and returns it to whoever is calling the list.

[...]
type
  TProcedimento = class
  private
  [...]
    FPRC_Produtos: TObjectList<TItemProcedimento>;
  public
  [...]
  function getPRC_Produtos: TObjectList<TItemProcedimento>;
  function criaProcedimentos: TObjectList<TProcedimento>;
  [...]

function TProcedimento.GetPRC_Produtos: TObjectList<TItemProcedimento>;
begin
  if not Assigned(FPRC_Produtos) then
    FPRC_Produtos:= TObjectList<TItemProcedimento>.Create;
  result := FPRC_Produtos;
end;

function TProcedimento.criaProcedimentos: TObjectList<TProcedimento>;
var
  IPR: TItemProcedimento;
  Procedimento: TProcedimento;
  ds: TFDQuery;
begin
  result := TObjectList<TProcedimento>.Create;
  ds := TFDQuery.Create(nil);
  ds.Connection := FDataSet.Connection;
  IPR := TItemProcedimento.Create(ds);
  try
    FDataSet.Close;
    FDataSet.Open('SELECT * FROM Procedimento');
    while not FDataSet.Eof do
    begin
      Procedimento := TProcedimento.Create(FDataSet);
      Procedimento.PRC_ID := FDataSet.FieldByName('PRC_ID').AsInteger;
      Procedimento.PRC_Nome := FDataSet.FieldByName('PRC_Nome').AsString;
      Procedimento.PRC_Duracao := FDataSet.FieldByName('PRC_Duracao')
        .AsDateTime;
      Procedimento.PRC_Preco := FDataSet.FieldByName('PRC_Preco').AsCurrency;
      Procedimento.PRC_Custo := FDataSet.FieldByName('PRC_Custo').AsCurrency;
      Procedimento.PRC_Consumo := FDataSet.FieldByName('PRC_Consumo').AsFloat;
      Procedimento.FPRC_Produtos := IPR.getItensProcedimento(FPRC_ID);
      result.Add(Procedimento);
      FDataSet.Next;
    end;
  finally
    FDataSet.Close;
    IPR.DisposeOf;
    ds.DisposeOf;
  end;
end;

I then use this property in a for-in loop to feed a list with the procs in my database

procedure TKBForm1.CarregaProcedimento;
var
  Procedimento: TProcedimento;
  Procs: TObjectList<TProcedimento>;
  [...]
begin
  Procs := TProcedimento.Create(DM.FDQ).criaProcedimentos;
  try
    LV_Procedimento.Items.Clear;
    LV_Procedimento.BeginUpdate;
    for Procedimento in Procs do
    begin
      with LV_Procedimento.Items.Add do
      [...]
  finally
    Procs.DisposeOf;
    Procedimento.GetPRC_Produtos.DisposeOf;
  end;
end;

But there's still leaks happening after this part runs:

73 - 88 bytes: TProcedimento x 1, TItemProcedimento x 2

How do I fix this?

0

There are 0 best solutions below