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

Проблема с сохранением объекта в TagObject


Марк

Вопрос

Версия XE 10.1 Berlin

При переводе своего приложения работающего под ОС Windows на Андроид столкнулся со следующей проблемой.

Я вывожу динамически ряд картинок на панель используя компонент TImage. Затем эти картинки могут быть отредактированы.

Для восстановления первоначальной картинки использовал свойство объекта TagObject, куда загружал TMemoryStream, в который сохранял первоначальный объект TBitmap.

Когда необходимо восстановить картинку, то из ее TagObject грузил в Image.Bitmap.

Под ОС Windows все работало отлично. Но вот когда попітался то же сделать для Android ничего не вышло.

Оказалось что попытка создать объект путем стандартной операции Image2.TagObject:=TMemoryStream.Create; ни к чему не приводит. Т.е. почему то TagObject остается равным NIL. TagObject можно присвоить только переменную потока после ее создания. Но  не скопировать ее значение в TagObject. Т.е. хранить не получается.

Не могли бы подсказать в чем дело. Прилагаю тестовый исходник. В нем есть 2 картинки и 2 кнопки. По первой кнопке сохраняем 1 картинку в TagObject второй. По второй кнопке загружаем из TagObject в Bitmap первую картинку.

При компиляции под виндой все ОК, под андроидом - ошибка.

 

TestStream.rar

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

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

  • 0

Действительно с TMemoryStream не работает, хотя вот так работает превосходно:

procedure TForm1.LoadButClick(Sender: TObject);
begin
  if Assigned(Image2.TagObject) then
    Image2.Bitmap.Assign(Image2.TagObject as TBitmap);
end;

procedure TForm1.SaveButClick(Sender: TObject);
begin
  Image2.TagObject:=Image1.Bitmap;
end;

Насколько критично для вас использовать прокладку в виде TMemoryStream? С TMemoryStream счетчик Image2.TagObject.RefCount по какой то причине обнуляется при выходе из процедуры SaveButClick. Не понятно.

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

Ага, разобрался - все дело в объявлении TagObject как "слабой" ссылки: [Weak] FTagObject: TObject, т.е. присвоение этому полю не увеличивает счетчик ссылок и объект будет жив только в пределах вашей процедуры. Грубо говоря в "слабой" мы храним объект до тех пор, пока он хранится где то еще.

Вам НЕОБХОДИМО создавать ГЛОБАЛЬНУЮ структуру для хранения этих объектов. К примеру сохранит картинки в TObjectList (модуль System.Generics.Collections), и после этого уже присваивать их в Image2.TagObject

Вот работающий вариант вашего кода:

unit copystream;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.Controls.Presentation, FMX.StdCtrls, FMX.Objects, System.Generics.Collections;


type
  TForm1 = class(TForm)
    Image1: TImage;
    Image2: TImage;
    SaveBut: TButton;
    LoadBut: TButton;
    procedure SaveButClick(Sender: TObject);
    procedure LoadButClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    FImageStore : TObjectList<TMemoryStream>;
  public
    { Public declarations }
  end;
var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.FormCreate(Sender: TObject);
begin
  FImageStore:=TObjectList<TMemoryStream>.Create;
end;

procedure TForm1.LoadButClick(Sender: TObject);
begin
  if Assigned(Image2.TagObject) then
    Image2.Bitmap.LoadFromStream(Image2.TagObject as TMemoryStream);
end;

procedure TForm1.SaveButClick(Sender: TObject);
Var LStream : TMemoryStream;
begin
  LStream:=TMemoryStream.Create;
  FImageStore.Add(LStream);
  try
    Image1.Bitmap.SaveToStream(LStream);
  finally
    Image2.TagObject:=LStream;
  end;
end;

end.

 

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

Спасибо за оперативный ответ! Только не понятно зачем TagObject у глобального компонента сделан "слабой ссылкой", т.е. фактически локальной переменной. А вот под виндой все сделано правильно. Вот Вам и многоплатформенность. Я конечно и сделал глобальную переменную(массив) где храню исходные картинки по индексам соответствующим индексам картинок.

Просто намного удобнее было использовать параметр конкретного компонента, тогда не надо делать индексный массив, а обращавшийся напрямую к параметру объекта. А стрим я храню вместо Bimap , т.к. при перегрузке картинки при использовании Assign, не перегружается картинка, если не очистить ее предварительно. Места как я понимаю занимают они одинаково.

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

В Windows ваш код работал "случайно", все дело в разных механизмах управления памятью на разных платформах и возможно в windows в какой то момент эти объекты тоже могут быть удалены.

Использование TMemoryStream, в вашем случае, увеличивает накладные расходы - производится копирование участков памяти, кстати так же как и при использовании Assign (не что иное как копирование). Возможно стоит использовать обычное присвоение? Т.е. в пределах приложения хранить одну единственную копию картинки, и по мере надобности присваивать ее (Image2.Bitmap:=Image2.TagObject as TBitmap) нужным элементам. 

По поводу вот этого

Цитата

А стрим я храню вместо Bimap , т.к. при перегрузке картинки при использовании Assign, не перегружается картинка, если не очистить ее предварительно.

можете привести пример кода или пример приложения, где воспроизводится данная проблема?

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

Только не понятно зачем TagObject у глобального компонента сделан "слабой ссылкой", т.е. фактически локальной переменной

Потому что это служебное поле, за которое объект-владелец не в ответе. Для иного вы можете использовать Image2.AddObject() - в этом случае Image2 будет знать о своих "детях" (Image2.Children в количестве Image2.ChildrenCount) и при самоубийстве покарает и детей. А в случае TagObject он проигнорирует содержащийся там объект  и возникнет утечка памяти. Воспринимайте TagObject как средство хранения ссылки на реально существующий в приложении объект, а не как место хранения самого объекта.

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

Еще раз спасибо за помощь! Я воспользовался Вашим советом и создал массив( у меня несколько панелей с картинками) с элементами TBitmap(FImageStore :array of TObjectList<TBitmap>;)

Затем для каждой панели я создаю объект TObjectList<TBitmap>.Create;

А каждую исходную картинку я гружу так

 n:=FImageStore[PageZal.PageIndex].Add(TBitmap.Create);
          try
            FImageStore[PageZal.PageIndex].Items[n].Assign(Bitmap);
          finally
            ImgList.TagObject:=FImageStore[PageZal.PageIndex].Items[n];
          end;

И когда нужно восстановить первоначальный вид то назначаю по клику на картинку через

Pic:=(Sender as TImage);

Pic.Bitmap.Assign(Pic.TagObect as TBitmap);

И все вроде работает.

Есть только еще один вопрос. При закрытии программы нужно ли освобождать этот массив объектов или нужно Освобождать Pic.TagObect ?

 

 

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

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

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

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

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

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

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

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

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

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

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