Major Опубликовано 11 марта, 2017 Поделиться Опубликовано 11 марта, 2017 Читал несколько статей по удалению объектов в 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 Major Опубликовано 11 марта, 2017 Автор Поделиться Опубликовано 11 марта, 2017 или лучше: 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 krapotkin Опубликовано 11 марта, 2017 Поделиться Опубликовано 11 марта, 2017 лучше всего просто хранить список созданных контролов, и уничтожать их по этому списку потому что иногда они могут оказаться в Children вовсе не там, куда вы их вставляли Цитата Ссылка на комментарий
0 Модераторы Равиль Зарипов (ZuBy) Опубликовано 11 марта, 2017 Модераторы Поделиться Опубликовано 11 марта, 2017 1 час назад, krapotkin сказал: лучше всего просто хранить список созданных контролов, и уничтожать их по этому списку согласен, так просто быстрей 1 час назад, krapotkin сказал: потому что иногда они могут оказаться в Children вовсе не там, куда вы их вставляли в коде явно указано куда их поместить, ошибки не будет @Major для просветления P.S. пользуйтесь кнопкой код "<>" для форматирования, я за вас все время это делать не буду Цитата Ссылка на комментарий
0 ENERGY Опубликовано 11 марта, 2017 Поделиться Опубликовано 11 марта, 2017 (изменено) Правильней будет сначала обнулить Родителя, затем вызвать 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). Изменено 11 марта, 2017 пользователем ENRGY Цитата Ссылка на комментарий
0 AlexShaman Опубликовано 11 марта, 2017 Поделиться Опубликовано 11 марта, 2017 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 Major Опубликовано 11 марта, 2017 Автор Поделиться Опубликовано 11 марта, 2017 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 kami Опубликовано 12 марта, 2017 Поделиться Опубликовано 12 марта, 2017 12 часов назад, ENRGY сказал: Правильней будет сначала обнулить Родителя, затем вызвать Free Нет, не правильнее. Объект может иметь ссылки не только в родителе (в списке его дочерних контролов), но и еще в куче мест, например - на которые он подписался (FreeNotification, к примеру) или на которые подписали его. Поэтому из родителя он исчезнет, Free - заNilит его текущую ссылку, но сам объект не удалится. 11 часов назад, Major сказал: меня почему-то идея, взятая из той статьи, приводит к Ссылка не на ту статью. В другой статье Ярослав упоминал Самый Правильный Метод - это вызвать myObject.Release. Это гарантированно удалит все ссылки на объект, присутствующие во внутренностях FMX. Понятное дело, что если объект присутствует где-то в списках (или просто отдельной переменной) в коде, созданном вами, то нужно заNilить эти ссылки (и удалить из списков) самостоятельно. ENERGY и Major 2 Цитата Ссылка на комментарий
0 Major Опубликовано 12 марта, 2017 Автор Поделиться Опубликовано 12 марта, 2017 kami, то есть самый верный способ для Андроида - это такой (если именно перебирать в цикле детей): for K := Layout1.ChildrenCount - 1 downto 0 do if Layout1.Children[K].ClassNameIs('TCircle') then (Layout1.Children[K] as TCircle).Release; Цитата Ссылка на комментарий
0 kami Опубликовано 12 марта, 2017 Поделиться Опубликовано 12 марта, 2017 (изменено) 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; Изменено 12 марта, 2017 пользователем kami Kitty, Rusland и Major 3 Цитата Ссылка на комментарий
Вопрос
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 еще что-то надо?
Ссылка на комментарий
9 ответов на этот вопрос
Рекомендуемые сообщения
Присоединяйтесь к обсуждению
Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.