Перейти к содержанию
Fire Monkey от А до Я
  • 0

[ANDROID] Создание и удаление компонентов run-time


Major

Вопрос

Читал несколько статей по удалению объектов в Android-приложениях, но до сих пор каша в голове.

Помогите прояснить ситуацию, пожалуйста.

Вот я создаю в run-time объекты:   

for J := 0 to Num - 1 do
begin
  MyCircle := TCircle.Create(Layout1);
  MyCircle.Parent := Layout1;
  MyCircle.Width := 100;
  MyCircle.Height := 100;
  MyCircle.Align := TAlignLayout.Left;
  MyCircle.Fill.Color := TAlphaColors.Red;
end;


Потом я хочу почистить слой от кружков. Так будет правильно?

for K := Layout1.ChildrenCount - 1 downto 0 do
  if Layout1.Children[K].ClassNameIs('TCircle') then
    (Layout1.Children[K] as TCircle).Free;

Или кроме Free еще что-то надо?

 

Ссылка на комментарий

Рекомендуемые сообщения

  • 0

или лучше:

for K := Layout1.ChildrenCount - 1 downto 0 do
if Layout1.Children[K].ClassNameIs('TCircle') then
  begin
  (Layout1.Children[K] as TCircle).DisposeOf;
  (Layout1.Children[K] as TCircle) := nil;
end;

или так:

for K := Layout1.ChildrenCount - 1 downto 0 do
begin
  if Layout1.Children[K].ClassNameIs('TCircle') then
  begin
    (Layout1.Children[K] as TCircle).Parent := nil;
    (Layout1.Children[K] as TCircle).Free;
    (Layout1.Children[K] as TCircle) := nil;
  end;
end;

 

Ссылка на комментарий
  • 0

лучше всего просто хранить список созданных контролов, и уничтожать их по этому списку

потому что иногда они могут оказаться в Children вовсе не там, куда вы их вставляли

Ссылка на комментарий
  • 0
  • Модераторы
1 час назад, krapotkin сказал:

лучше всего просто хранить список созданных контролов, и уничтожать их по этому списку

согласен, так просто быстрей

1 час назад, krapotkin сказал:

потому что иногда они могут оказаться в Children вовсе не там, куда вы их вставляли

в коде явно указано куда их поместить, ошибки не будет

@Major 

для просветления

P.S. пользуйтесь кнопкой код  "<>" для форматирования, я за вас все время это делать не буду

Ссылка на комментарий
  • 0

Правильней будет сначала обнулить Родителя, затем вызвать Free.  Чтобы уменьшить счетчик ARC. При присваивании класса этот счетчик увеличивается (т.к. появилась еще одна ссылка на него), и при обнулении ссылки - уменьшается. Можно поставить точку останова на деструктор  вашего класса, чтобы точно узнать вызывается он или нет.

При вызове Free на ARC компиляторах (а это iOS, Android и будущий Linux) - компилятор вызовов Free  переделает в обычное обнуление.

procedure TObject.Free;
begin
// under ARC, this method isn't actually called since the compiler translates
// the call to be a mere nil assignment to the instance variable, which then calls _InstClear
{$IFNDEF AUTOREFCOUNT}
  if Self <> nil then
    Destroy;
{$ENDIF}
end;

Кстати в вашем случае, как написали выше лучше использовать дженерик TObjectList из System.Generics.Collections. Кстати он уничтожает объекты при помощи DisposeOf.

 

Насчет DisposeOf.  Эта конструкция явно вызывается деструктор объекта, но не освобождает память которая выделена под объект. Т.е. при обращении к такому "уничтоженному" объекту, не произойдет нарушения доступа (Access Violation). Такой объект называется зомби объект.

Вот интересная статья, очень советую  http://www.gunsmoker.ru/2013/05/modern-delphi.html

Там описаны и слабые weak ссылки ,которые не увеличивают счетчик - удобно использовать когда классы хранят ссылки друг на друга..

У себя в проекте я в основном использую free (или FreeAndNil - в случае когда нужно проверять был ли уже создан ли объект (MyTObj <> nil).

Изменено пользователем ENRGY
Ссылка на комментарий
  • 0
4 часа назад, Major сказал:

или лучше:


for K := Layout1.ChildrenCount - 1 downto 0 do
if Layout1.Children[K].ClassNameIs('TCircle') then
  begin
  (Layout1.Children[K] as TCircle).DisposeOf;
  (Layout1.Children[K] as TCircle) := nil;
end;

или так:


for K := Layout1.ChildrenCount - 1 downto 0 do
begin
  if Layout1.Children[K].ClassNameIs('TCircle') then
  begin
    (Layout1.Children[K] as TCircle).Parent := nil;
    (Layout1.Children[K] as TCircle).Free;
    (Layout1.Children[K] as TCircle) := nil;
  end;
end;

 

    Layout1.DeleteChildren;
    Layout1.Repaint;

Ссылка на комментарий
  • 0
5 часов назад, Равиль Зарипов (ZuBy) сказал:

@Major

для просветления

 

у меня почему-то идея, взятая из той статьи, приводит к exception class EArgumentOutOfRangeException with message 'Argument out of range' с выбросом в

procedure TListHelper.CheckItemRange(AIndex: Integer);
begin
  CheckItemRangeInline(AIndex);
end;

Мой код:

for K := Layout1.ChildrenCount - 1 downto 0 do
begin
  if Layout1.Children[K].ClassNameIs('TCircle') then
  begin
    (Layout1.Children[K] as TCircle).Parent := nil;
    (Layout1.Children[K] as TCircle).Free;
  end;
end;

 

Ссылка на комментарий
  • 0
12 часов назад, ENRGY сказал:

Правильней будет сначала обнулить Родителя, затем вызвать Free

Нет, не правильнее.

Объект может иметь ссылки не только в родителе (в списке его дочерних контролов), но и еще в куче мест, например - на которые он подписался (FreeNotification, к примеру) или на которые подписали его. Поэтому из родителя он исчезнет, Free - заNilит его текущую ссылку, но сам объект не удалится.

11 часов назад, Major сказал:

меня почему-то идея, взятая из той статьи, приводит к

Ссылка не на ту статью. В другой статье Ярослав упоминал Самый Правильный Метод - это вызвать myObject.Release. Это гарантированно удалит все ссылки на объект, присутствующие во внутренностях FMX. Понятное дело, что если объект присутствует где-то в списках (или просто отдельной переменной) в коде, созданном вами, то нужно заNilить эти ссылки (и удалить из списков) самостоятельно.

Ссылка на комментарий
  • 0

kami,

то есть самый верный способ для Андроида - это такой (если именно перебирать в цикле детей):

for K := Layout1.ChildrenCount - 1 downto 0 do
  if Layout1.Children[K].ClassNameIs('TCircle') then
    (Layout1.Children[K] as TCircle).Release;

 

Ссылка на комментарий
  • 0
56 минут назад, Major сказал:

то есть самый верный способ для Андроида

Поправка: это самый верный способ для FMX, вне зависимости от платформы.

Емнип, у TCircle нет наследников. Ну и - для использования Release не обязательно приводить тип к истинному классу объекта.

Поэтому сей код можно записать так (не проверял в IDE, но если скомпилируется - значит всё нормально):

for k:=Layout1.ChildrenCount-1 downto 0 do
  if Layout1.Children[k] is TCircle then
    Layout1.Children[k].Release;

 

Изменено пользователем kami
Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...