#delphi #firemonkey #delphi-10.4-sydney
#delphi #firemonkey #delphi-10.4-сидней
Вопрос:
Вот класс, который я создал для добавления метки TLabel в TTrackBar. Метка показывает значение trackbar при перетаскивании, а затем исчезает. Экземпляр создается во время выполнения, а родительский элемент устанавливается в форму. Он работает нормально, но выдает ошибку при закрытии приложения, если панель отслеживания все еще существует. Однако нет никаких проблем, если панель отслеживания освобождается во время выполнения, а затем приложение закрывается. При отладке этой строки при закрытии приложения (FLabel.Бесплатно;) Я вижу, что FLabel и данные в нем все еще существуют, но он по-прежнему выдает эту ошибку. Я обеспокоен тем, что если я просто удалю эту строку, то при освобождении объекта во время выполнения произойдет утечка памяти. Я попытался изменить его на if Assigned(FLabel), а затем на FLabel.Бесплатно; но без изменений. Я знаю, что это должно быть как-то связано с тем фактом, что родительский элемент метки был установлен.
unit TrackBarLabelUnit;
interface
uses
System.Types, System.Classes, System.SysUtils, FMX.Types, FMX.StdCtrls,
FMX.Controls;
type
TValueToString = function(AValue : Single) : String of object;
TTrackBarLabel = class(TTrackBar)
private
FLabel : TLabel;
FSuffix : String;
FTimer : TTimer;
FOffset : Integer;
FValueToString : TValueToString;
procedure TimerTimer(Sender: TObject);
protected
procedure ParentChanged; override;
procedure DoTracking; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Suffix : String read FSuffix write FSuffix;
property LabelOffset : Integer read FOffset write FOffset;
property ValueToString : TValueToString write FValueToString;
end;
implementation
constructor TTrackBarLabel.Create(AOwner: TComponent);
begin
inherited;
FLabel := TLabel.Create(nil);
FLabel.Visible := False;
FTimer := TTimer.Create(nil);
FTimer.Interval := 100;
FTimer.Enabled := False;
FTimer.OnTimer := TimerTimer;
FSuffix := '';
FOffset := 22;
end;
destructor TTrackBarLabel.Destroy;
begin
FLabel.Free; // EInvalidPointer error here when application is closed
FTimer.Free;
inherited Destroy;
end;
procedure TTrackBarLabel.ParentChanged;
begin
inherited;
FLabel.Parent := Parent;
end;
procedure TTrackBarLabel.DoTracking;
begin
inherited;
if not Assigned(Thumb) then Exit;
FLabel.Visible := True;
FLabel.Tag := 10;
FLabel.Opacity := 1;
if Assigned(FValueToString) then
FLabel.Text := FValueToString(Value) FSuffix
else
FLabel.Text := FloatToStrF(Value, ffFixed, 12, 1) FSuffix;
if Orientation = TOrientation.Horizontal then begin
FLabel.Position.X := Position.X Thumb.Position.X
(Thumb.Width - FLabel.Width) * 0.5;
FLabel.Position.Y := Position.Y FOffset;
FLabel.TextSettings.HorzAlign := TTextAlign.Center;
end else begin
FLabel.Position.X := Position.X FOffset;
FLabel.Position.Y := Position.Y Thumb.Position.Y - 2;
FLabel.TextSettings.HorzAlign := TTextAlign.Leading;
end;
FTimer.Enabled := False;
FTimer.Enabled := True;
end;
procedure TTrackBarLabel.TimerTimer(Sender: TObject);
begin
FLabel.Tag := FLabel.Tag - 1;
FLabel.Opacity := FLabel.Tag * 0.2;
if FLabel.Tag < 0 then begin
FLabel.Visible := False;
FTimer.Enabled := False;
end;
end;
end.
Ответ №1:
Чаще всего исключение с недопустимым указателем означает, что вы пытаетесь освободить объект дважды.
Проблема в этом случае заключается в том, что элемент управления освобождает своих дочерних элементов при его освобождении. Поэтому, когда форма освобождается, она также освобождает TLabel
. Таким образом, когда TTrackBarLabel.Destroy
выполняется your, your FLabel
является висячим указателем, и вы не должны этого делать FLabel.Free
.
Все разработчики Delphi знают, что компонент освобождает принадлежащие ему компоненты при его освобождении. Менее известный факт, что элемент управления также освобождает своих дочерних элементов.
В вашем случае вы можете просто удалить FLabel.Free
. Однако это приведет к утечке памяти, если вы никогда не устанавливали свойство FLabel
‘s Parent
.
Чтобы убедиться, что метка автоматически освобождается, когда панель треков, сделайте панель треков владельцем метки:
FLabel := TLabel.Create(Self);
В качестве отступления, ваше предложение
if Assigned(FLabel) then
FLabel.Free;
не поможет, потому FLabel
что при возникновении ошибки появляется висячий указатель (это не nil
так).
Кроме того, в Delphi вы никогда не пишете
if Assigned(FLabel) then
FLabel.Free;
потому TObject.Free
что в основном это if Assigned then Destroy
так
if Assigned(FLabel) then
FLabel.Free;
означает
if Assigned(FLabel) then
if Assigned(FLabel) then
FLabel.Destroy;
что очень глупо.
Комментарии:
1. Спасибо. Итак, в итоге мне не нужно освобождать его самостоятельно, и чтобы убедиться в этом, я делаю панель треков владельцем метки. Я сделал это изначально, но хотел освободить его сам, чтобы убедиться, что он был освобожден. Я где-то читал, что создание метки с nil означало бы, что я буду нести ответственность за ее освобождение сам, но, похоже, это не так, поскольку форма все равно освобождает своих дочерних элементов. Почему нет проблемы с TTimer? Я знаю, что родительский элемент не задан как метка, но если я создаю его с помощью трекбара в качестве владельца, у меня все равно не возникает никаких проблем при его освобождении.
2. @XylemFlow: (1) Правильно. Удалите свой
FLabel.Free
и напишитеTLabel.Create(Self)
, чтобы сделать панель треков владельцем метки. (2) Действительно, «все» знают, что компонент освобождает принадлежащиеOwner
ему компоненты () при его освобождении, но не так хорошо известно, что элемент управления освобождает свои дочерние компоненты (Parent
) при его освобождении. (3)TTimer
Это компонент (TComponent
), но не элемент управления (TControl
). Это не то, что отображается на экране; у него нет родительского элемента. Если вы создадите его с панелью отслеживания в качестве ее владельца, оно действительно будет освобождено, когда панель отслеживания будет, но когда вы это сделаетеFTimer.Free
…3. … владелец (панель отслеживания) уведомляется об этом, и таймер удаляется из списка принадлежащих владельцу компонентов.
4. Спасибо. Похоже, тогда мне вообще не нужен деструктор. Я предполагаю, что деструктор действительно нужен только для освобождения объектов, у которых нет владельца, или для очистки массива.
5. Технически вам нужен деструктор, но вам не нужно вызывать его самостоятельно, потому что библиотека времени выполнения сделает это за вас (в данном случае, когда владелец освобожден).