Autor Téma: Interface a správa života objektů  (Přečteno 2913 krát)

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Interface a správa života objektů
« kdy: 11-10-2012, 12:21:17 »
Nikdy jsem s interface moc nedělal, protože jakmile jsem s nimi zkusil něco trochu složitějšího, hned jsem se začal dostávat do potíží s jejich vytvářením a uvolňováním (buď jsem uvolňoval moc, nebo málo). Ale říkám si, třeba je na čase do toho konečně proniknout.

Chci mít třídu, která se chová podobně jako TObjectList s OwnsObjects=True, ale nebude obsahovat instance třídy TObject ale instance podporované interfacem IMujInterface. A nevím, jak dosáhnout toho, abych v destruktoru té třidy dosáhl zničení instancí, když k nim mám jenom interface, který se pochopitelně ničit nedá.

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Interface a správa života objektů
« Odpověď #1 kdy: 11-10-2012, 12:43:08 »
Pro začátek by stačila takováhle blbost...
Kód: Delphi [Vybrat]
  1. type
  2.   IMujInterface = interface(IUnknown)
  3.     ['{da33e00e-b694-4819-86cf-f02c8a12f034}']
  4.     procedure Writeln;
  5.     end;
  6.   TMujClass = class(TInterfacedObject, IMujInterface)
  7.     private
  8.       fCislo: integer;
  9.       procedure Writeln;
  10.     public
  11.       constructor Create(ACislo: integer);
  12.     end;
  13.  
  14. // ...
  15.  
  16. constructor TMujClass.Create(ACislo: integer);
  17. begin
  18.   inherited Create;
  19.   fCislo := ACislo;
  20. end;
  21.  
  22. procedure TMujClass.Writeln;
  23. begin
  24.   System.Writeln(fCislo);
  25. end;

Jak získat k instanci TMujClass odkaz na IMujInterface tak, abych instance TMujClass zůstala naživu?
Kód: Delphi [Vybrat]
  1. var
  2.   Trida: TMujClass;
  3.   Inst: IMujInterface;
  4.  
  5. begin
  6.   Trida := TMujClass.Create(1);
  7.   if Supports(TObject(Trida), IMujInterface, Inst) then
  8.     begin
  9.     IMujInterface(Inst).Writeln;
  10.     //Inst := nil; // Tohle uvolni i instanci tridy, takze mi v Trida zustane akorat neplatny pointer
  11.     end;
  12.   Trida.Free; // Tohle spadne v TInterfacedObject.BeforeDestruction na tom, že RefCount není nula.
  13. end;
Je mi samozřejmě jasné, že jsem mohl rovnou použít
Kód: Delphi [Vybrat]
  1. IMujInterface(Trida).Writeln;
místo toho IFu, a to pak nespadne - akorát přetypovávám něco, co dopředu nevím, jestli ten interface obsahuje nebo neobsahuje (řekněme, že jsem Trida dostal jako obyčejný TObject, např. v rámci TNotifyEvent).

Offline zdenek

  • Plnoletý
  • ***
  • Příspěvků: 139
  • Karma: 8
Re:Interface a správa života objektů
« Odpověď #2 kdy: 11-10-2012, 12:46:12 »
TInterfaced objekt se ti zruší sám, jakmile se zruší všechny reference na interface.

Uprav si:

Kód: Delphi [Vybrat]
  1. Inst:=TMujClass.cretate(1);

Odstraň

Kód: Delphi [Vybrat]
  1. Trida.Free;

Aby ti zůstal naživu, tak si musíš buď udržovat v nějaké proměnné odkaz na interface nebo jde zavolat (myslím) TMujClass.AddRef pro umělé zvenutí refcount. Pak ho ale musíš také ručně snížit a objekt se ti sám uvolní.
« Poslední změna: 11-10-2012, 12:50:47 od zdenek »

Offline TLama

  • Guru
  • *****
  • Příspěvků: 597
  • Karma: 31
    • Verze Delphi: 7, 2009, XE3
« Poslední změna: 11-12-2012, 15:53:16 od TLama »

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Interface a správa života objektů
« Odpověď #4 kdy: 11-10-2012, 13:06:52 »
TInterfaced objekt se ti zruší sám, jakmile se zruší všechny reference na interface.
Jo, přesně na to si stěžuju.

Citace
Aby ti zůstal naživu, tak si musíš buď udržovat v nějaké proměnné odkaz na interface nebo jde zavolat (myslím) TMujClass.AddRef pro umělé zvenutí refcount. Pak ho ale musíš také ručně snížit a objekt se ti sám uvolní.
To ale neřeší můj problém! Můj problém je, že někde zvenku mi přijde třída, která implementuje určitý interface. Život té třídy se má řídit tou třídou, tzn. tím, jestli na ní nekdo dá nebo nedá Destroy. Interface používám čistě jen k tomu, abych jednu a tu samou funkcionalitu mohl použít pro tisíc různých tříd, které třeba nemají žádného společného předka kromě TObject.

Pokročilý problém je, že tu třídu mohu zrušit ze dvou míst, podle toho, které přijde dřív - buď uvolněním třídy (což zneplatní interface), nebo uvolněním interfacu (což zneplatní třídu). Ale volitelně, podle toho, odkud to zavolám (asi jako s běžnou třídou uloženou v TObjectList si "můžu vybrat", jestli ji zničím v programu pomocí List[0].Free, nebo ji zničím v TObjectListu pomocí List.OwnsObjects := True; List.Delete(0);)

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Interface a správa života objektů
« Odpověď #5 kdy: 11-10-2012, 13:11:11 »
TInterfaceList ?
Pokud řeší moji otázku, tak nevidím jak.

Citace
Jinak "instanci" interface se říká reference...
Proč ne, mohu tomu tak říkat i já. Ale myslím si, že "instance třídy podporující interface X" je mým potřebám bližší než "reference na interface X" - pro mě jsou prvotní ty instance třídy, ty chci zpracovávat, že z nich občas budu využívat jeden interface, který implementují.

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Interface a správa života objektů
« Odpověď #6 kdy: 11-10-2012, 13:33:06 »
Momentálně vidím jediné řešení, a to nepoužívat TInterfacedObject s jeho uvolňováním po zrušení poslední reference, ale něco jiného s vlastním řešením _Release. Ale to pak znamená, že se moje zamýšlená třída nedá použít na nic, co je odvozené od TInterfacedObject (to by mi až tak nevadilo) a že každý uživatel bude muset naimplementovat IUnknown (to už mi vadí víc).

Offline zdenek

  • Plnoletý
  • ***
  • Příspěvků: 139
  • Karma: 8
Re:Interface a správa života objektů
« Odpověď #7 kdy: 11-10-2012, 13:47:38 »
To ale neřeší můj problém! Můj problém je, že někde zvenku mi přijde třída, která implementuje určitý interface. Život té třídy se má řídit tou třídou, tzn. tím, jestli na ní nekdo dá nebo nedá Destroy. Interface používám čistě jen k tomu, abych jednu a tu samou funkcionalitu mohl použít pro tisíc různých tříd, které třeba nemají žádného společného předka kromě TObject.

Pokročilý problém je, že tu třídu mohu zrušit ze dvou míst, podle toho, které přijde dřív - buď uvolněním třídy (což zneplatní interface), nebo uvolněním interfacu (což zneplatní třídu). Ale volitelně, podle toho, odkud to zavolám (asi jako s běžnou třídou uloženou v TObjectList si "můžu vybrat", jestli ji zničím v programu pomocí List[0].Free, nebo ji zničím v TObjectListu pomocí List.OwnsObjects := True; List.Delete(0);)

Hlavním úkolem TInterfaced je právě, že se nepracuje s třídou. Ta funguje jen jako jakási factory pro implmentaci daného objektu. takže v těchto případech většinou "chodí zvenku" interface místo objektu.

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3343
  • Karma: 139
    • Verze Delphi: D2007, XE3, DX10
Re:Interface a správa života objektů
« Odpověď #8 kdy: 11-10-2012, 13:50:14 »
Momentálně vidím jediné řešení, a to nepoužívat TInterfacedObject s jeho uvolňováním po zrušení poslední reference, ale něco jiného s vlastním řešením _Release. Ale to pak znamená, že se moje zamýšlená třída nedá použít na nic, co je odvozené od TInterfacedObject (to by mi až tak nevadilo) a že každý uživatel bude muset naimplementovat IUnknown (to už mi vadí víc).
Bezne se to resi tak, ze se prekryji metody toho TInterfacedObject, takze ta trida si urci, jak bude jeji zivotni cyklus spravovan, napr.
Kód: Delphi [Vybrat]
  1. function TVariantCollection._AddRef: Integer;
  2. begin
  3.   if (RefCounted) then
  4.     Result := inherited _AddRef
  5.   else
  6.     Result := 2;
  7. end;
  8.  
  9. function TVariantCollection._Release: Integer;
  10. begin
  11.   if (RefCounted) then
  12.     Result := inherited _Release
  13.   else
  14.     Result := 1;
  15. end;
  16.  
A pokud nekdo s interfacy pracuje v multithreadovem prostredi, tak si je prekryva taky a reference counting dela pomoci InterlockedIncrement/Decrement.

Offline zdenek

  • Plnoletý
  • ***
  • Příspěvků: 139
  • Karma: 8
Re:Interface a správa života objektů
« Odpověď #9 kdy: 11-10-2012, 15:00:57 »
A pokud nekdo s interfacy pracuje v multithreadovem prostredi, tak si je prekryva taky a reference counting dela pomoci InterlockedIncrement/Decrement.

Toto už je zařízeno v Delphi, jelikož to ještě chce jeden krůček navíc, aby to v multithreading prostředí fungovalo.

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3343
  • Karma: 139
    • Verze Delphi: D2007, XE3, DX10
Re:Interface a správa života objektů
« Odpověď #10 kdy: 11-10-2012, 15:10:37 »
Toto už je zařízeno v Delphi
Skutecne. Interfacy se tusim objevily v D3 - to to tam bylo vzdycky? Jestli jo, tak proc bychom to prekryvali, to jsem z toho blazen :o

Offline zdenek

  • Plnoletý
  • ***
  • Příspěvků: 139
  • Karma: 8
Re:Interface a správa života objektů
« Odpověď #11 kdy: 11-10-2012, 15:15:20 »
Skutecne. Interfacy se tusim objevily v D3 - to to tam bylo vzdycky? Jestli jo, tak proc bychom to prekryvali, to jsem z toho blazen :o
Tak starý delphi nevím. V D7 už to je.

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1559
  • Karma: 37
    • Pepak.net
Re:Interface a správa života objektů
« Odpověď #12 kdy: 11-10-2012, 15:16:45 »
V D5 taky.