• 0
gonzales

деструктор своего компонента

Вопросы

Доброго времени суток!

Не знаю, в какой раздел правильнее написать, потому как проблема из разных областей.

Собственно проблема в следующем

Есть базовый класс, TEssence

type
  TEssence = class(TRectangle)
  private
    procedure EssenceMouseEnter(Sender: TObject);
    procedure EssenceMouseLeave(Sender: TObject);
  public
    EssenceName: TLabel;
    EssenceImage: TImage;
    ImageSize: integer;
    constructor Create(AOwner: TComponent); Overload;
    constructor CreateCustom(AOwner: TComponent; X: Single; Y: Single; W: Single; H: Single; R: Single; Str_T: Single;
      Str_C: cardinal; Fill_C: cardinal; FontColor: cardinal; FontSize: Single; Im_size: Single; text: string);
      Overload;
    destructor Destroy; Overload;
    procedure SetName(Name: string);
    procedure SetIcon(iconnumber: integer);
  end;

Есть наследники от базового класса. Среди них есть есть класс TCamera

type
  TCamera = class(TEssence)
  public
    Thread: TCameraThread;
    Camera_id: byte;
    Snapshot_url: string;
    GetRecords_url: string;
    Record_url: string;
    DelFile_url: string;
    Camera_Image: TImage;
    CameraActive: boolean;
    CameraValue: TCircle;
    buttonLayout: TLayout;
    DetailsButton: TButton;
    RecordsCount: integer;
    RecordsArray: array of String;
    constructor Create(AOwner: TComponent); Overload;
    constructor CreateCustom(AOwner: TComponent; X: Single; Y: Single; W: Single; H: Single; R: Single; Str_T: Single;
      Str_C: cardinal; Fill_C: cardinal; FontColor: cardinal; FontSize: Single; Im_size: Single; text: string);
      Overload;
    destructor Destroy; Overload;
    procedure CameraSetURL;
    procedure StartCamera;
    procedure StopCamera;
    procedure CameraClick(Sender: TObject);
    procedure CameraRestart(Sender: TObject);
    procedure DownloadClick(Sender: TObject);
    procedure DeleteClick(Sender: TObject);
    procedure DetailClick(Sender: TObject);
  end;
destructor TCamera.Destroy;
begin
  StopCamera;
  buttonLayout.Release;
  CameraValue.Release;
  inherited;
end;

procedure TCamera.StopCamera;
begin
  if Thread<>nil then
  begin
  Thread.IdHTTP.Disconnect;
  Thread.Terminate;
  end;
  if Camera_Image<>nil then
  begin
  Camera_Image.Release;
  Camera_Image := nil;
  CameraActive := false;
  SetLength(RecordsArray, 0);
  end;
end;

Он отличается от других подобных, что в при создании экземпляра в нем создается поток TCameraThread, в котором я получаю изображение с камеры.

type
  TCameraThread = class(TThread)
  private
    aStream: TMemoryStream;
    newStream: TMemoryStream;
  public
    IdConnection: TIdConnectionIntercept;
    IdHTTP: TIdHTTP;
    url: string;
    bmp: tbitmap;
    constructor Create(abmp: tbitmap); Overload;
    destructor Destroy; Overload;
    procedure Execute; override;
    procedure Receive(ASender: TIdConnectionIntercept; var ABuffer: TIdBytes);
  end;

 

constructor TCameraThread.Create(abmp: tbitmap); // (abmp: tbitmap; aurl: string);
begin
  inherited Create(true);
  bmp := abmp;
  FreeOnTerminate := true;
  aStream := TMemoryStream.Create;
  newStream := TMemoryStream.Create;
  IdHTTP := TIdHTTP.Create;
  IdConnection := TIdConnectionIntercept.Create;
  IdHTTP.Intercept := IdConnection;
  IdConnection.OnReceive := Receive;
end;

destructor TCameraThread.Destroy;
begin
 bmp.Free;
 bmp:=nil;
 aStream.Free;
 aStream:=nil;
 newStream.Free;
 newStream:=nil;
 IdHTTP.Free;
 IdHTTP:=nil;
 IdConnection.Free;
 IdConnection:=nil;
 inherited;
end;

И есть процедура FreeRoom, которой я пользуюсь для того, чтобы убивать все элементы

procedure TForm1.FreeRoom;
var
  i: integer;
begin
  Form1.MasterTimer.Enabled := false;
  for i := Form1.RoomsScrollBox.ComponentCount - 1 downto 0 do
  begin
    if Form1.RoomsScrollBox.Components[i] is TEssence then
    begin
      (Form1.RoomsScrollBox.Components[i] as TEssence).Release;
    end;
  end;
  SetLength(Form1.RoomElements, 0);
  CurrentRoomElement := 0;
end;

Я всегда думал, что при такой конструкции будет следующий порядок, вызовется деструктор TCamera, в котором сработает функция StopCamera, которая остановит и уничтожит поток и уничтожится экземпляр класса TCamera. И все это нормально (наверное) работало до тех пор, пока я не прикрутил сервис  IFMXApplicationEventService. Задача была крайне простой, при сворачивании приложения уничтожить все объекты и потоки, а при разворачивании опять все запустить.

Соответственно была сделана вот такая простая контрукция

procedure TForm1.FormCreate(Sender: TObject);
var
  aFMXApplicationEventService: IFMXApplicationEventService;
begin
 if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationEventService, IInterface(aFMXApplicationEventService)) then
    aFMXApplicationEventService.SetApplicationEventHandler(HandleAppEvent);
end;

function TForm1.HandleAppEvent(AAppEvent: TApplicationEvent; AContext: TObject): boolean;
begin
  case AAppEvent of
    TApplicationEvent.BecameActive:
      begin
       Form1.CheckDefaultRoom;
      end;
    TApplicationEvent.WillBecomeInactive:
      begin
        MasterTimer.Enabled := false;
        Form1.FreeRoom;
      end;
  end;
  Result := true;
end;

Но происходит странное. Если у меня есть экземпляр TCamera с потоком внутри, то сворачивается приложение нормально, но уже не разворачивается, просто висит в памяти, или показывает черный экран. Если среди объектов нет TCamera, то все отрабатывает правильно. Судя по всему дело именно в потоке, который каким-то образом валит приложение. Еще одна странность, что я не могу отдебажить приложение, я поставил точку остановки на деструкторе, но программа не останавливается в нем, причем не только в Андроид, но и в винде.

Третий день уже бьюсь, подскажите, кто чем может, буду признателен за любую информацию.

Заранее спасибо!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

14 ответов на этот вопрос

  • 0

тут много чего хочется прямо поправить

самое первое - убрать в классе TForm1 ссылки на экземпляр этого класса Form1

2. отказаться от idHTTP в пользу THttpClient

3. release обычно не требуется. если все написано хорошо, то FreeAndNil() самое то. а если плохо, то до конца все равно не спасет

4. архитектурно не очень идея визуальному компоненту привинчивать насмерть невизуальный функционал, ну да бог с ним

5. не надо думать что Potok.Terminate вообще хоть что-то делает, кроме как Potok.Terminated:=True. Это если у вас в Execute цикл, вы можете проверять if Terminated then exit... не  более того.

6. если вы создаете компоненты динамически, то держите ссылки на них у себя, чтобы не делать for i := Form1.RoomsScrollBox.ComponentCount-1 if ...

7. концепция потока и его родителя должна быть такова: подразумеваем, что поток может пережить родителя, поэтому почаще надо проверять, if terminated то ничего не делая поскорее выходить из потока...

8. при уничтожении компонента нужно не только убить его FreeAndNil(component), но сначала compjnent.Parent := NIL;

при соблюдении этих правил не требуется абсолютно никаких извращений при проектировании 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
1 час назад, krapotkin сказал:

3. release обычно не требуется. если все написано хорошо, то FreeAndNil() самое то. а если плохо, то до конца все равно не спасет

Вродебы сейчас православно делать .DisposeOf
По крайней мере, ембакадеро так советовали, дальше всю работу делает сборщик падали мусора.

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

8. при уничтожении компонента нужно не только убить его FreeAndNil(component), но сначала compjnent.Parent := NIL;

А можно плиз тут подробнее, для чего это делается?
Я всегда считал что компонент сам это делает перед уничтожением.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

DisposeOf нужно делать чтобы ВЫЗВАТЬ деструктор 

а для ARC обычно НЕ НУЖНО вызывать деструктор))

объект уничтожается, когда заканчиваются ссылки на него

Parent держит ссылку на объект. пока ее не удалишь, то и объект не захочет удаляться сам. а пока он не удаляется, не почистит и Parent. и вот тогда идет ненужная принудиловка с разными извращениями.

 

Изменено пользователем krapotkin

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • -1
6 минут назад, krapotkin сказал:

Parent держит ссылку на объект. пока ее не удалишь, то и объект не захочет удаляться сам. а пока он не удаляется, не почистит и Parent. и вот тогда идет ненужная принудиловка с разными извращениями.

Так ведь счетчик ссылок же!
Нафиг он тогда нужен, если все равно приходится ссылки за него подчищать??!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

DisposeOf к сожалению я не могу использовать, так как надо уничтожить объект при нажатии на него самого. Поэтому использую Release, как отложенный деструктор, как только объект освободится, он будет уничтожен. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

С potok.terminated не понял, почему так, ведь при создании выставил флаг freeonterminate. По идее все должно быть, может конечно он не отрабатывает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Обычно с FreeOnTerminate проблем нет, он отрабатывает.
Проблема 99% где-то до этого. Возможно камера как-то стопорится/блокируется при уходе приложения в фон.
Попробуйте использовать компонент TfgApplicationEvents от Ярослава Бровина. Там есть возможность перехватывать такие события и соотв. реагировать на них.

Изменено пользователем Barbanel

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
5 часов назад, Barbanel сказал:

счетчик ссылок же!

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

но для кроссплатформенной совместимости я чаще всего пишу freeAndNil() . Там вызывается Free но на ARC внутри Free - пусто

4 часа назад, gonzales сказал:

С potok.terminated не понял, почему так, ведь при создании выставил флаг freeonterminate

хоть 10 раз freeOnTerminate

Загляните в код TThread.Terminate. Он не делает НИЧЕГО! вы же не показали, что у вас флаг Terminated как-то используется в Execute... До этого момента, видя, что в других местах вы гуляете по граблям, я уверен, что и тут - тоже.

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

чтобы уничтожить объект из обработчика объекта, нужно обладать смелостью и задором, конечно))). Но можно просто

TTask.Run(procedure begin TThread.Synchronize(procedure begin comp1.parent := nil;  FreeAndNil(comp1); end) end);

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
13 часов назад, krapotkin сказал:

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

Ну так я это и хочу сделать.

13 часов назад, krapotkin сказал:

TTask.Run(procedure begin TThread.Synchronize(procedure begin comp1.parent := nil;  FreeAndNil(comp1); end) end);

Прикольно, ща попробую.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
14 часов назад, krapotkin сказал:

хоть 10 раз freeOnTerminate

Помогло

  Thread.IdHTTP.Disconnect;
  Thread.Terminate;
  Thread:=nil;
  Thread.Free;

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
27 минут назад, gonzales сказал:

Помогло


  Thread.IdHTTP.Disconnect;
  Thread.Terminate;
  Thread:=nil;
  Thread.Free;

 

Не совсем корректно.

Как уже было сказано, Thread.Terminate всего лишь выставляет флаг для остановки потока, но не останавливает его.
Поток сам себя останавливает, основываясь на этом флаге.
После того как вы вызвали Thread.Terminate, необходимо дождаться пока поток действительно остановится.
Либо, присваивать заранее потоку FreeOnTerminate чтобы он сам себя убил после остановки, и не вызвать его деструктор.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
58 минут назад, Barbanel сказал:

Не совсем корректно.

Как уже было сказано, Thread.Terminate всего лишь выставляет флаг для остановки потока, но не останавливает его.
Поток сам себя останавливает, основываясь на этом флаге.
После того как вы вызвали Thread.Terminate, необходимо дождаться пока поток действительно остановится.
Либо, присваивать заранее потоку FreeOnTerminate чтобы он сам себя убил после остановки, и не вызвать его деструктор.

Я же писал, флаг FreeOnTerminate стоит, но не отрабатывает в Андроид. Поэтому пришлось убивать

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
29 минут назад, gonzales сказал:

Я же писал, флаг FreeOnTerminate стоит, но не отрабатывает в Андроид. Поэтому пришлось убивать

Это очень странно.
Проверю в течение дня.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

прекрасно все отрабатывает в андроид. я проверял. просто поставьте Create(true) наконец

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти

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

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