Instrukcją wiążąca With a uzyskanie adresu klasy.

0

Zastanawiam się jak najprościej uzyskać adres zmiennej w takim przypadku:

with TJakasKlasa.Create(Application) do
begin
  ZrobTo;
  Update(Self_TJakasKlasa); 
end;

//zamiast robić tak 

v := TJakasKlasa.Create(Application);
v.ZrobTo;
Update(v);
0

Nijak – jeśli używasz with w połączeniu z konstruktorem. Nie używaj takiej konstrukcji, bo raz, że nie masz dostępu do referencji, a dwa, że ten kod jest niebezpieczny i w przypadku wyjątku spowoduje wyciek pamięci. Używanie lokalnych zmiennych nie boli, tak samo jak konstrukcji try finally do zabezpieczania przed wyciekami.


Tak z ciekawości, skoro Delphi posiada ”inline variables” – czy poniższy kod jest kompilowalny?

with var Foo: TFoo := TFoo.Create() do
try
  Foo.DoThis();
  Foo.DoThat();

  DoSomething(Foo);
finally
  Foo.Free();
end;

Miałbyś fikuśne rozwiązanie swojego problemu.

0

Niestety mam wersję Tokyo 10.2.3 a takie deklarowanie zmiennych jest w nowszym RIO :(

zrobiłem tak:

type
  TFramePlus = class(TFrame)
    FrameAdapter: TsFrameAdapter;
  private
    { Private declarations }
  public
    function GetSelf: Pointer;
    procedure AfterCreation; virtual; 
    procedure BeforeDestroy; virtual; 
  end;

function TFramePlus.GetSelf: Pointer;
begin
  Result := Self;
end;


procedure CreateTabAndOpen(APageControl: TsPageControl; AFrame: TClassOfFrame; Typ: TTypeOperation; ATabName: string);

  function NameTab: string;
  begin
    case Typ of
      Add: Result := TFramePlus(AFrame).Name+'_ADD';
      Edit: Result := TFramePlus(AFrame).Name+'_EDIT';
    end;
  end;

var
  TabSheet: TsTabSheet;
  Component: TComponent;
begin
  Component := APageControl.FindComponent(NameTab);
  If Component <> nil then
    APageControl.ActivePage := TsTabSheet(Component)
  else
  begin
    TabSheet := TsTabSheet.Create(APageControl);
    TabSheet.Name := NameTab;
    TabSheet.Caption := ATabName;
    TabSheet.UseCloseBtn := true;
    TabSheet.PageControl := APageControl;
    with AFrame.Create(Application) do
    begin
      FormData.SkinManager.UpdateScale(GetSelf);
      Parent := TabSheet;
      APageControl.ActivePage := TabSheet;
    end;
  end;
end;

Funkcją GetSelf pobieram referencję

0

No dobra, jakieś to rozwiązanie jest, ale po co na siłę używasz with, skoro w tym przypadku potrzebujesz referencji? Utwórz tę ramkę w sposób standardowy, z wykorzystaniem zmiennej lokalnej – w niczym to nie przeszkodzi.

0

Czyli takie rozwiązanie jest lepsze i pewniejsze wraz z użyciem try except?

begin
  Component := APageControl.FindComponent(NameTab);
  If Component <> nil then
    APageControl.ActivePage := TsTabSheet(Component)
  else
  begin
    TabSheet := TsTabSheet.Create(APageControl);
    try
      TabSheet.Name := NameTab;
      TabSheet.Caption := ATabName;
      TabSheet.UseCloseBtn := true;
      TabSheet.PageControl := APageControl;
      Frame := AFrame.Create(Application);
      try
        FormData.SkinManager.UpdateScale(Frame);
        Frame.Id := Id;
        Frame.Parent := TabSheet;
        APageControl.ActivePage := TabSheet;
      except
        Frame.Free;
        raise;
      end;
    except
      TabSheet.Free;
      raise;
    end;
  end;
end;
0

Czyli takie rozwiązanie jest lepsze i pewniejsze wraz z użyciem try except?

Nie do końca. Blok try except end służy do obsługi wyjątków a samo zwalnianie obiektu należy umieścić w bloku try finally end. Możesz je zagnieździć

try
    MyClass := TMYclass.Create(Self);
    try
      .....
    finally
       MyClass.Free;
    end;
except
    // obsługa wyjątku.
end;

można zastosować odwrotne zagnieżdżenie

try
    MyClass := TMYclass.Create(Self);
    try
      .....
    except
      // obsługa wyjątku
    end;
finally
     MyClass.Free;
end;

Stosowałem takie rozwiązanie w sytuacji kiedy obsługa wyjątku wymagała dostępu do obiektu generującego wyjątek .
Ale wtedy ryzykujemy że obsługa wyjątku w sekcji except może generować kolejny wyjątek .

Wtedy można zrobić podwójne zagnieżdżenie

try 
  try
    MyClass := TMYclass.Create(Self);
    try
      .....
    except
      // obsługa pierwotnego wyjątku
    end;
  finally
     MyClass.Free;   
  end;
except
  // obsługa ewentualnego wyjątku  powstałego w obsłudze pierwotnego wyjątku 
end;

Taki wariant po pierwsze zapewnia możliwość dostępu do obiektu generującego wyjątek na etapie obsługi pierwotnego wyjątku, a po drugie zapewnia że cała sekcja jest zamknięta, czyli zwalnia obiekt i obsłuży wszystkie wyjątki bez względu na na miejsce ich powstania/

0

Zwalnianie komponentu – nawet w przypadku wyjątku – nie jest konieczne, bo tym zajmie się komponent będący jego rodzicem. Tak więc spokojnie można usunąć z kodu wywołania Free, tak samo jak oba bloki try except.

0

Zwalnianie komponentu – nawet w przypadku wyjątku – nie jest konieczne, bo tym zajmie się komponent będący jego rodzicem. Tak więc spokojnie można usunąć z kodu wywołania Free, tak samo jak oba bloki >try except.

W pierwszym punkcie masz rację, ale to był szkolny przykład, nie każdy obiekt musi mieć rodzica. Parentem może być nil albo konstruktor klasy nie ma "parenta".
A w drugim,to nie rozumiem dlaczego uważasz że można usunąć bloki try except

0
grzegorz_so napisał(a):

W pierwszym punkcie masz rację, ale to był szkolny przykład, nie każdy obiekt musi mieć rodzica. Parentem może być nil albo konstruktor klasy nie ma "parenta".

Tak, ale mój post jest odpowiedzią na post OP, a nie na Twój. ;)

A uściślając – nie rodzicem, a właścicielem, bo chodzi o parametr Owner konstruktora, a nie o właściwość Parent. Przy czym w kodzie podanym przez OP, są tworzone komponenty wizualne, zawsze posiadające zarówno właściciela, jak i rodzica (używane jest albo Application, albo APageControl).

A w drugim,to nie rozumiem dlaczego uważasz że można usunąć bloki try except

Bo są zbędne – jeśli komponent zostanie prawidłowo stworzony, to jego zwolnieniem zajmie się właściciel. A jeśli nie, to konstruktor zadba o dealokację komponentu niepoprawnie utworzonego, a kolejne instrukcje (i kolejne wywołania konstruktorów) zostaną pominięte, więc nie będzie czego sprzątać.

Tym bardziej że jedyne co się znajduje wewnąrz except to nadmiarowy Free oraz raise, który puszcza wyjątek dalej. Dokładnie to samo można osiągnąć usuwając te bloki – działanie się nie zmieni, a kodu będzie mniej.

0

Dziękuję wszystkim za podpowiedzi. Wracając do tematu kod musi być z try.. except jak poniżej:

begin
  Component := APageControl.FindComponent(NameTab);
  If Component <> nil then
    APageControl.ActivePage := TsTabSheet(Component)
  else
  begin
    TabSheet := TsTabSheet.Create(APageControl);
    try
      TabSheet.Name := NameTab;
      TabSheet.Caption := ATabName;
      TabSheet.UseCloseBtn := true;
      TabSheet.PageControl := APageControl;
      Frame := AFrame.Create(TabSheet);
      FormData.SkinManager.UpdateScale(Frame);
      Frame.Id := Id;
      Frame.Parent := TabSheet;
      APageControl.ActivePage := TabSheet;
    except
      APageControl.Pages[TabSheet.PageIndex].Free;
      raise;
    end;
  end;
end;

Ponieważ @furious programming ma rację można wywalić zwalniania obiektów bo tym zajmą się wskazane komponenty podczas tworzenia Frame i Tabsheet. Ale i to ale jest najważniejsze. W sytuacji gdy np Frame wygeneruje wyjątek. To TabSheet i Frame jest już stworzone i zwolnione by została dopiero po zwolnieniu APageControl czyli w moim przypadku po zamknięciu aplikacji. Co jest bezsensu dla mnie. Dlatego muszę użyć sekcję try .. except z Free bo inaczej otrzymam błąd przy kolejnej próbie wykonania procedury ( taka nazwa komponentu już istnieje - a dokładnie zobaczę pustą stronę TabSheet ).

Jedynie Frame.Free mogę pominąć bo tym zajmie się Tabsheet po wywołaniu tabsheet.free.

0

Niechcąc zakładać kolejnego posta powiąże go z tym. Powyżej przedstawiłem dynamiczne tworzenie zakładki i ramki. I teraz na tej ramce mam przycisk z poniższym kodem:

procedure Button;
begin
  if Parent.ClassName = 'TsTabSheet' then
    TsPageControl(Parent.Parent).Pages[TsTabSheet(Parent).PageIndex].Free;
end;

Która ma za zadanie zamknąć zakładkę TabSheet wraz z ramką. I to prawie działa bo zamyka ale po drodze otrzymuję błędy AV. No tak sobie myślę że chce zniszczyć Frame które jeszcze wykonuje kod w Button. Jakie jest najlepsze rozwiązanie takiej sytuacji?

  • Wysłać komunikat do formy głównej która zniszczy zakładkę po np 100 ms?
0

Kombinujesz taki kod powinien działać:

procedure TFrame1.Button1Click(Sender: TObject);
begin
  Self.Parent.Free;
end;

Rodzicem Frame jest TabSheet, który zadba o zwolnienie Frame.

0

@kAzek: na pewno powinno działać? Żaden obiekt nie może sam siebie zwolnić z poziomu swojej metody, a do tego się sprowadza to co pokazałeś. IMO trzeba by ”asynca” użyć, aby to było możliwe.

Pod Lazarusem skorzystałbym z QueueAsyncCall, a w Delphi nie wiem co tam macie.

0

Działa i nie sam siebie zwalnia tylko rodzica a rodzic dopiero komponenty. Jest tak zwalnia TabSheet który zwalnia Frame a dopiero Frame zwalnia ten Button, który to wywołał (i inne komponenty).

EDIT: @furious programming
Proste sprawdzenie kod tworzenia TabSheet i Frame:

procedure TForm4.Button1Click(Sender: TObject);
var
  TabSheet: TTabSheet;
  Frame: TFrame1;
  Name: string;
  i: Integer;
begin
  TabSheet:= TTabSheet.Create(PageControl1);
  try
    i:= 0;
    repeat
      Inc(i);
      Name:= 'TabSheet' + IntToStr(i);
    until (PageControl1.FindChildControl(Name) = nil);
    TabSheet.Name:= Name;
    TabSheet.Caption:= Name;
    Frame:= TFrame1.Create(nil); //specjalnie bez własciciela
    Frame.Parent:= TabSheet;
    TabSheet.PageControl:= TPageControl(TabSheet.Owner);
    //raise Exception.Create('Error');
  except
    on E: Exception do begin
      TabSheet.Free;
      ShowMessage(E.Message);
    end
  end;
end;

Button na Frame zwalniający w wersji wszystko ok:

procedure TFrame1.Button1Click(Sender: TObject);
begin
  Self.Parent.Free;
end;

w wersji z wyciekiem pamięci

procedure TFrame1.Button1Click(Sender: TObject);
var
  oldParent: TWinControl;
begin
  oldParent:= Self.Parent;
  Self.Parent:= nil; //pozbywamy się rodzica. Nie ma rodzica ani właściciela więc zwolnienie TabSheet nie zwolni Frame
  oldParent.Free; //zwalniamy TabSheet
end;

Gdyby kod Self.Parent.Free działał źle to albo by się wykrzaczał albo powodował wyciek a nic takiego nie ma miejsca.

0
kAzek napisał(a):

Działa i nie sam siebie zwalnia tylko rodzica a rodzic dopiero komponenty. Jest tak zwalnia TabSheet który zwalnia Frame a dopiero Frame zwalnia ten Button, który to wywołał (i inne komponenty).

No właśnie, czyli de facto sam siebie zwalnia, tyle że pośrednio – wszystkie kontrolki zostaną zwolnione (łącznie z przyciskiem wywołującym) zanim zdarzenie OnClick zakończy swoje działanie. No ale skoro działa i nie leci wyjątek to widać implementacja pozwala na taki zabieg (jest to dozwolone).

Napisałeś, że „powinno działać”, jakbyś nie był pewny – dlatego dopytałem, bo sam też nie byłem pewny czy to zadziała.

0
kAzek napisał(a):

Kombinujesz taki kod powinien działać:

procedure TFrame1.Button1Click(Sender: TObject);
begin
  Self.Parent.Free;
end;

Rodzicem Frame jest TabSheet, który zadba o zwolnienie Frame.

Niestety ten kod jest nie poprawny. Owszem zamyka zakładkę ale gdy w pagecontrol próbujemy myszką przejść na inną zakładkę otrzymujemy błąd 'Invalid pointer' czego nie ma gdy użyjemy tej mojej zawiłej instrukcji:

TsPageControl(Parent.Parent).Pages[TsTabSheet(Parent).PageIndex].Free;

A sam błąd wywalał komonent z AlphaSkins - już zostało zlokalizowane i naprawione.

1 użytkowników online, w tym zalogowanych: 0, gości: 1