TPngImage.Destroy () вызывается, когда TPngImage выходит из области видимости, если TPngImage.Free () не вызывается - PullRequest
0 голосов
/ 29 сентября 2018

У меня есть функция, которая принимает TPicture в качестве параметра и возвращает TPngImage.Чтобы сохранить исходное изображение, я создаю TPngImage и копирую TPicture в TPngImage, применяю эффект и возвращаю TPngImage.Как и так.

function Effect( const Value : TPicture ) : TPngImage;
  var
    AnImage : TPngImage;

  begin
    if( Value.Graphic is TPngImage ) then
      begin
        AnImage := TPngImage.Create();
        AnImage.Assign( TPngImage( Value ) );
        //Apply effect
        Result := AnImage;
        //AnImage.Free(); //error
      end;
  end;

procedure TForm11.Button1Click( Sender : TObject );
  begin
    Image2.Picture.Assign( Effect( Image1.Picture ) );
  end;

Когда я создаю объект, когда он освобождает созданный объект.Я не могу вызвать TPngImage.Free () в функции, так как она уничтожает объект перед присваиванием.Так как же освободить созданный объект?Вызывает ли TPngImage свой деструктор, когда объект выходит из области видимости?Насколько я вижу, не освобождение объекта приведет к утечке памяти.

Ответы [ 2 ]

0 голосов
/ 29 сентября 2018

То, как вы это делаете, приводит к утечке.

Вы можете вернуть объект из функции, подобной этой, но после использования (в этом случае, после того, как вы присвоили его Image2.Picture, вы должныFree it. Но вы можете сделать это, только если у вас есть ссылка на него . Так сделайте это:

procedure TForm11.Button1Click(Sender: TObject);
var
  EffectImage: TPngImage;
begin
  EffectImage := Effect(Image1.Picture);
  Image2.Picture.Assign(EffectImage);
  EffectImage.Free;
end;

Обратите внимание, что если изображение не PNG,Ваша Effect функция не вернет ничего действительного. Эту проблему вы также должны решить.

В качестве альтернативы, я бы сделал что-то вроде этого:

procedure ProcEffect(InPicture, OutPicture: TPicture);
var
  AnImage : TPngImage;
begin
  if InPicture.Graphic is TPngImage then
  begin
    AnImage := TPngImage.Create;
    try
      AnImage.Assign(InPicture);
      //Apply effect
      OutPicture.Graphic := AnImage; // same as OutPicture.Assign(AnImage);
    finally
      AnImage.Free;
    end;
  end;
end;

И затемпозвоните с помощью:

procedure TForm11.Button1Click(Sender: TObject);
begin
  ProcEffect(Image1.Picture, Image2.Picture);
end;    
0 голосов
/ 29 сентября 2018

Ваш код содержит несколько ошибок:

if( Value.Graphic is TPngImage ) then

Если TPicture вызывающего абонента еще не содержит TPNGImage, вы вообще ничего не возвращаете.Result не определено.

На самом деле вам вообще не следует проверять тип Graphic.Различные TGraphic классы могут быть назначены друг другу, преобразовывая их данные изображения из одного формата в другой, поэтому вы должны позволить этому преобразованию произойти, когда это возможно.

AnImage.Assign( TPngImage( Value ) );

Вы осуществляете типизациюСам TPicture.Вместо этого вам нужно набрать Graphic.

Result := AnImage;
//AnImage.Free();

Для этого необходимо, чтобы вызывающий абонент взял на себя владение TPNGImage и освободил его, что, как правило, является плохим дизайном.

Image2.Picture.Assign( Effect( Image1.Picture ) );

В данном случае вызывающая сторона не вступает во владение возвращенной TPngImage, поэтому она просочилась.

Если вы хотите вернуть новую TPNGImage, попробуйте это вместо:

function Effect(Value : TPicture) : TPngImage;
begin
  Result := TPngImage.Create;
  try
    if (Value.Graphic <> nil) and (not Value.Graphic.Empty) then
    begin
      Result.Assign(Value.Graphic);
      //Apply effect
    end;
  except
    Result.Free;
    raise;
  end;
end;

Или

function Effect(Value : TPicture) : TPngImage;
begin
  Result := nil;
  if (Value.Graphic <> nil) and (not Value.Graphic.Empty) then
  begin
    Result := TPngImage.Create;
    try
      Result.Assign(Value.Graphic);
      //Apply effect
    except
      Result.Free;
      raise;
    end;
  end;
end;

В любом случае, вы можете сделать это так:

procedure TForm11.Button1Click(Sender : TObject);
var
  AImage: TPngImage;
begin
  AImage := Effect(Image1.Picture);
  try
    Image2.Picture.Assign(AImage);
  finally
    AImage.Free;
  end;
end;

Однако, лучший дизайн - этоновый TPngImage не вернуть вообще.Передайте 2 TPicture объектов и позвольте Effect() манипулировать ими по мере необходимости:

procedure Effect(Input, Output : TPicture);
var
  AnImage : TPngImage;
begin
  AnImage := TPngImage.Create;
  try
    if (Input.Graphic <> nil) and (not Input.Graphic.Empty) then
    begin
      AnImage.Assign(Input.Graphic);
      //Apply effect
    end;
    Output.Assign(AnImage);
  finally
    AnImage.Free;
  end;
end;

или

procedure Effect(Input, Output : TPicture);
var
  AnImage : TPngImage;
begin
  if (Input.Graphic <> nil) and (not Input.Graphic.Empty) then
  begin
    AnImage := TPngImage.Create.Create;
    try
      AnImage.Assign(Input.Graphic);
      //Apply effect
      Output.Assign(AnImage);
    finally
      AnImage.Free;
    end;
  end else
    Output.Assign(nil);
end;

Тогда вы можете сделать это:

procedure TForm11.Button1Click(Sender : TObject);
begin
  Effect(Image1.Picture, Image2.Picture);
end;
...