Autor Téma: Zničenie objektov v ComboBox.Items  (Přečteno 3256 krát)

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6161
  • Karma: 44
    • Verze Delphi: W10 + D11
Zničenie objektov v ComboBox.Items
« kdy: 24-01-2016, 22:55:36 »
Kód: Delphi [Vybrat]
  1. procedure TGlobalVar.FreeAndNilComboBoxObj(ComboBox: TComboBox);
  2. var
  3.   I: Integer;
  4. begin
  5.   if (ComboBox.Items.Count = 0) or (ComboBox.Name = 'cbbFOC') then
  6.     Exit;
  7.  
  8.   for I := 0 to ComboBox.Items.Count -1 do
  9.   begin
  10.     if (ComboBox.Items.Objects[I] = nil) then  // Niektoré ComboBox-y majú spoločný TStringList
  11.     begin
  12.       ComboBox.Items.Objects[I].Free;
  13.       ComboBox.Items.Objects[I] := nil;
  14.     end;
  15.   end;
  16.  
  17.   ComboBox.Items.Clear;
  18. end;
  19.  
Ak tam nedám "if (ComboBox.Items.Objects = nil) then " tak to zahlási pokus o uvoľnenie už neexistujúceho objektu. Rozumel by som tomu, keby tam bolo <> nil !!!
Formulár sa automaticky prechádza a táto procedúra sa uplatní na každý jeho ComboBox.
« Poslední změna: 24-01-2016, 23:01:07 od Stanislav Hruška »
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline Ondřej Pokorný

  • Guru
  • *****
  • Příspěvků: 815
  • Karma: 59
    • Verze Delphi: Primárně Lazarus, jinak D7 až aktuální
    • Kluug.net
Re:Zničenie objektov v ComboBox.Items
« Odpověď #1 kdy: 24-01-2016, 23:03:44 »
Podívej se co dělá Free a hned zjistíš v čem je chyba - v ComboBox.Items.Objects[I ] máš už uvolněnou instanci objektu, která ale není vynulovaná (nastavená na nil).
« Poslední změna: 24-01-2016, 23:06:37 od oxo »
Embarcadero Technology Partner

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Zničenie objektov v ComboBox.Items
« Odpověď #2 kdy: 25-01-2016, 07:23:50 »
Doplním k oxo, že důvod, proč ve tvé verzi to "funguje", je ten, že metoda Free napřed udělá test na Self <> nil a pokračuje, jen pokud podmínka je splněna. Tzn. tvůj program se dá přepsat jako if (Objekt = nil) and (Objekt <> nil) then DelejNecoSObjektem(Objekt).

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Zničenie objektov v ComboBox.Items
« Odpověď #3 kdy: 25-01-2016, 07:30:48 »
Ještě k tomu dvojímu uvolnění: Základní problém je v tom, že se o tom uvolnění objektu nedozvíš. Tvůj kód vypadá asi takhle (zkráceno na nutné minimum):
Kód: Delphi [Vybrat]
  1. var
  2.   MyObject: TMyObject;
  3.   List1, List2: TStringList;
  4. begin
  5.   List1 := TStringList.Create;
  6.   List2 := TStringList.Create;
  7.   MyObject := TMyObject.Create;
  8.   List1.AddObject('1', MyObject);
  9.   List2.AddObject('2', MyObject);
  10.   List1.Objects[0].Free;
  11.     // Zde se uvolnil nejen List1.Objects[0], ale také List2.Objects[0]
  12.     // a MyObject, protože všechno to jsou ukazatelé na stejný objekt.
  13.   List1.Objects[0] := nil;
  14.     // Jenže nil se zapsalo pouze do List1.Objects[0]. List2.Objects[0]
  15.     // a MyObject jsou odlišné proměnné, které si samozřejmě zachovají
  16.     // původní hodnotu, přestože objekt, na který ukazují, už neexistuje.
  17.   List2.Objects[0].Free;
  18.     // Podruhé uvolňuješ už uvolněný objekt!!!
  19. end;
Je potřeba to řešit tak, že ty instance budeš skladovat a uvolňovat v nějakém společném seznamu a v List1, List2 na ně budou jen nějaké odkazy (spíš indexy do toho společného seznamu než přímo ukazatele, aby se dalo rozpoznat, jestli už objekt byl nebo nebyl uvolněn); nebo, samozřejmě, ty instance ve společném seznamu nebudeš za běhu uvolňovat, uvolníš je až při konci aplikace.

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3343
  • Karma: 139
    • Verze Delphi: D2007, XE3, DX10
Re:Zničenie objektov v ComboBox.Items
« Odpověď #4 kdy: 25-01-2016, 08:33:28 »
Kód: Delphi [Vybrat]
  1.     if (ComboBox.Items.Objects[I] = nil) then  // Niektoré ComboBox-y majú spoločný TStringList
Tohle jsi tu prece uz probiral: ty nemuzes rusit objekty (polozky), jejichz nejsi vlastnikem. Vetsinou se udela jeden vlastnik, ktery se bude starat o zivotni cyklus vlastnenych polozek a ostatni ho budou sdilet. V kazdem pripade, pokud budes chtit mit soucasne nekolik "zivych" sdileni, musis udelat nejaky mechanismus propagovani zmeny.

AFAIR, tak se ti to nelibilo a poradit sis nedal.

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6161
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Zničenie objektov v ComboBox.Items
« Odpověď #5 kdy: 25-01-2016, 09:02:32 »
Citace
AFAIR, tak se ti to nelibilo a poradit sis nedal.
Nie že nepáčilo, ale nevedel som ako na to a zdalo (a ešte zdá) sa mi to príliš komplikované.
Citace
Základní problém je v tom, že se o tom uvolnění objektu nedozvíš.
Veď to.
Napadlo ma, že môžem Combox-y plniť hodnotami a nie odkazmi. V takom prípade problém zmizne.
« Poslední změna: 25-01-2016, 09:04:51 od Stanislav Hruška »
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3343
  • Karma: 139
    • Verze Delphi: D2007, XE3, DX10
Re:Zničenie objektov v ComboBox.Items
« Odpověď #6 kdy: 25-01-2016, 09:46:40 »
Nie že nepáčilo, ale nevedel som ako na to a zdalo (a ešte zdá) sa mi to príliš komplikované.
No zalezi, co to vlastne delas - moc jasne mi to neni.

Pokud chces sdilet nejaky ciselnik na vice formularich soucasne, tak je nejjednodusii si napr. na datamodulu u delat instanci TStringList a tu naplnit daty.
A pri otevreni formulare ho priradit do Items u comboboxu, pri ruseni formulare su Items nevsimat. A pak se musis postarat o plneni/uvolnovani toho TStringListu.


Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6161
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Zničenie objektov v ComboBox.Items
« Odpověď #7 kdy: 25-01-2016, 10:16:08 »
Ja to mám v rámci jedného formulára. Ukážka
Kód: Delphi [Vybrat]
  1.       FilllstLandlordOriginal;
  2.       cbbFirst.Items.Assign(lstLandlordOriginal);
  3.       cbbFirst.ItemIndex := 0;
  4. ....
  5.       cbbSecond.Items.Assign(lstLandlordOriginal);  // Ten Assign som dal len teraz
  6.       cbbSecond.Items.Delete(0);
  7.       cbbSecond.ItemIndex := 0;
  8.  
Ten Assign zjavne nezaberá. Vytvorím si dva zoznamy a bude pokoj.


Hop, zrada bude asi niekde inde :)  Idem to hľadať. A skúsim rozmýšľať.

« Poslední změna: 25-01-2016, 10:31:38 od Stanislav Hruška »
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6161
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Zničenie objektov v ComboBox.Items
« Odpověď #8 kdy: 25-01-2016, 11:43:49 »
Jedna chyba objavená. Ak čosi vymažem v CmoboBox.Items, tak sa o to musím postarať aj vTStringList
Kód: Delphi [Vybrat]
  1.       cbbSecond.Items := lstLandlordSecond;
  2.       lstSecond := TList<Integer>(lstLandlordSecond.Objects[0]);
  3.       FreeAndNil(lstSecond);            // Toto som nemal
  4.       cbbSecond.Items.Delete(0);
  5.       lstLandlordSecond.Delete(0);  // Toto som nemal
  6.  
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline Ondřej Pokorný

  • Guru
  • *****
  • Příspěvků: 815
  • Karma: 59
    • Verze Delphi: Primárně Lazarus, jinak D7 až aktuální
    • Kluug.net
Re:Zničenie objektov v ComboBox.Items
« Odpověď #9 kdy: 26-01-2016, 00:27:07 »
Doplním k oxo, že důvod, proč ve tvé verzi to "funguje", je ten, že metoda Free napřed udělá test na Self <> nil a pokračuje, jen pokud podmínka je splněna. Tzn. tvůj program se dá přepsat jako if (Objekt = nil) and (Objekt <> nil) then DelejNecoSObjektem(Objekt).

Tím jsi mě nedoplnil, ale vyřešil Standův domácí úkol, který jsem mu naznačil :) Asi jsem naznačoval moc špatně  ::)
Embarcadero Technology Partner

Offline Ondřej Pokorný

  • Guru
  • *****
  • Příspěvků: 815
  • Karma: 59
    • Verze Delphi: Primárně Lazarus, jinak D7 až aktuální
    • Kluug.net
Re:Zničenie objektov v ComboBox.Items
« Odpověď #10 kdy: 26-01-2016, 00:29:42 »
Jedna chyba objavená. Ak čosi vymažem v CmoboBox.Items, tak sa o to musím postarať aj vTStringList

Naštuduj si TComponent.FreeNotification. Občas se to na podobné hrátky hodí.
Embarcadero Technology Partner

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 6161
  • Karma: 44
    • Verze Delphi: W10 + D11
Re:Zničenie objektov v ComboBox.Items
« Odpověď #11 kdy: 26-01-2016, 10:04:33 »
Citace
Naštuduj si TComponent.FreeNotification
Asi budem musieť. Už sa mi dosť často stáva, že je potreba sa pozrieť do zdrojákov Delphi.
W10 64b, Delphi 10.4, FireBird 3.08
Expert na kladenie nejasne formulovaných otázok.

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3343
  • Karma: 139
    • Verze Delphi: D2007, XE3, DX10
Re:Zničenie objektov v ComboBox.Items
« Odpověď #12 kdy: 26-01-2016, 12:05:55 »
Naštuduj si TComponent.FreeNotification. Občas se to na podobné hrátky hodí.
Jestli si to pamatuju, tak FreeNotification/RemoveNotification/Notification s informaci o operaci opInsert/opRemove je mechanismus u kontejneru, ze se meni seznam jimi vlastnenych prvku (TComponent). Ale TStrings a jeho potomci nemaji za predka obecny kontejner TCollection ani TComponent... (TStrings je potomek TPersistent)

Bohuzel, jako skoro vzdy, je Delphi, co se tyka navrhu, inkonzistentni: notifikace  OnChanging a OnChanged jsou definovany jen u TStringList, ktery systematicky vola Changing/Changes v operacich s obsahem, ale abstraktni predek TStrings ani jeho specializovani potomci jako napr. TCustomComboBoxStrings nic takoveho nema  :(

Pokud by byl ten sdileny seznam na bazi TStringList, tak by se to dalo vyuzit k tomu, ze primarne a exkluzivne by se smazal/pridal prvek v nem a pri obsluze jeho udalosti OnChanged by se provedla synchronizace navazanych comboboxu - zapamatoval by se vybrany prvek - nejjednoduzsi by bylo priradit cely, aktualni obsah TStringListu a obnovit vybrany prvek.
« Poslední změna: 26-01-2016, 12:08:11 od pf1957 »

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3343
  • Karma: 139
    • Verze Delphi: D2007, XE3, DX10
Re:Zničenie objektov v ComboBox.Items
« Odpověď #13 kdy: 26-01-2016, 12:15:40 »
Bohuzel, jako skoro vzdy, je Delphi, co se tyka navrhu, inkonzistentni: notifikace  OnChanging a OnChanged jsou definovany jen u TStringList, ktery systematicky vola Changing/Changes v operacich s obsahem, ale abstraktni predek TStrings ani jeho specializovani potomci jako napr. TCustomComboBoxStrings nic takoveho nema  :(
Zrejme to prameni z toho, ze ty standardni widgety jako combobox apd. jsou postaveny nad wokennim API a vlastni polozky jsou ulozeny "uvnitr" windows a windows notifikaci o odstraneni polozky neposilaji - asi predpokladaji, ze kdyz jim nekdo poslal message CB_DELETESTRING nebo WM_DELETEITEM, ze vi, ze dela zmenu obsahu.

Ovsem to nic nemeni na mem nazoru, ze takovy mechanismum mel existovat na urovni abstraktniho predka a ne az u TStringList a specializovane wrappery kolem wokenniho API ho meli implementovat.


Offline Ondřej Pokorný

  • Guru
  • *****
  • Příspěvků: 815
  • Karma: 59
    • Verze Delphi: Primárně Lazarus, jinak D7 až aktuální
    • Kluug.net
Re:Zničenie objektov v ComboBox.Items
« Odpověď #14 kdy: 26-01-2016, 18:03:45 »
Jedna chyba objavená. Ak čosi vymažem v CmoboBox.Items, tak sa o to musím postarať aj vTStringList

Naštuduj si TComponent.FreeNotification. Občas se to na podobné hrátky hodí.

Koukám, že máš v tom ComboBox.Items.Objects[] TList<>, který nedědí od TComponent, takže to tento problém neřeší.

Ale stejně se na to koukni ;) Na internetu najdeš spoustu informací a je to více než jednoduché - prostě si zaregistruješ notifikaci, že komponenta v Objects[] byla zabita a tak si tu referenci vynuluješ.

Pokud ty objekty ale stejně zabíjíš jen z jednoho místa (a neumírají ti kvůli něčemu zvenčí), tak máš lepší si to ohlídat tam. T.j. přesně, jak jsi udělal tady
Kód: Delphi [Vybrat]
  1.       lstLandlordSecond.Delete(0);  // Toto som nemal
Embarcadero Technology Partner