Avatar billede hrc Mester
03. oktober 2013 - 11:19 Der er 3 kommentarer og
1 løsning

Interfaces i praksis

Jeg har altid brugt konstruktionen med et dataobjekt og en dataliste

type
  TData = class
  private
  public
    { ... data ... }
  end

  TDataList = class(TObjectList)
  private
  public
    property Items[aIndex: integer]: TData read GetItems; default;
  end;

.. altså et objekt i en objektliste. Nu vil jeg prøve at lave det med interfaces.

Jeg skal skanne et katalog for filer (medcom-filer) og her er opgaven at identificere dem.
Status er:
- filer af ukendt format
- filer af medcom-format der ikke understøttes
- filer af medcom-format der understøttes.

Derfor syntes jeg det var smart at lave interfaces til hver af de standarder jeg skal håndtere:

type
  IStd1 = interface
    ['{GUID}']
    function GetStd1: IXMLStd1;
  end;

  IStd2 = interface
    ['{GUID}']
    function GetStd2: IXMLStd2;
  end;

  IStd3 = interface
    ['{GUID}']
    function GetStd3: IXMLStd3;
  end;

  TFiles = class(TInterfacedObject,IStd1,IStd2,IStd3)
  private
  public
    function GetStd1: IXMLStd1;
    function GetStd2: IXMLStd2;
    function GetStd3: IXMLStd3;
  end;

  TFilesList = class(TObjectList)
  private
  public
    constructor Create(aPath: string);
    procedure Initialize; // scan katalog
    property Items[aIndex: integer]: TFiles read GetItems; default;
  end;

Efterfølgende løber jeg listen igennem og propper objekterne ind på hver sin linje i et ListView (hægter det på TListItem.Data).

Her går det galt. Hvis jeg inde i en procedure gør følgende:

procedure frmMain.lvFilesOnSelectItem(Sender: TObject; Item: TListItem; Selected: Boolean);
var
  MyData: TData;
  I_Std1: IStd1;
begin
  MyData := Item.Data
  I_Std1 := MyData as IStd1;
  I_Std1.GetStd1 ...
end;

... så går Garbage Collectoren igang med at frigive I_Std1 ved exit af proceduren - hvilket åbenbart er TData-objektet i listen => Access violations næste gang jeg tilgår objektet, og absolut ikke hvad jeg havde forventet!

Kan man forhindre garbage collectoren i at rydde op Hvorfor gør den i det hele taget noget? Reference count må da være større end 0?

Er det fordi jeg gemmer objekter der understøtter interfaces eller er det fordi jeg skal bytte TObjectList ud med TInterfaceList? Et eksempel derpå, kan findes her: http://www.swissdelphicenter.ch/torry/showcode.php
Avatar billede hrc Mester
03. oktober 2013 - 11:21 #1
Linket til Torry dur ikke. Man må søge på overskriften "...use Interfaces and TInterfaceList?"
Avatar billede hrc Mester
08. oktober 2013 - 12:43 #2
Lukker igen. Løsningen var at bruge TInterfaceList.
Avatar billede mbsnet Nybegynder
08. oktober 2013 - 20:14 #3
Lyder rigtig spændende hrc.
Håber du vil fortælle os andre lidt om dette, hvis du kommer længere med det !

mvh
morten
Avatar billede hrc Mester
09. oktober 2013 - 08:05 #4
Nåe ja. Det gjorde jeg jo, men du gir' mig et vink fordi jeg altid gør opmærksom på manglende løsninger, når andre blot lukker, ikke?

Jeg kan stadig have mine lister, men kører jeg med TObjectList (synes ikke generics matcher funktionaliteten), så kommer der AV-fejl når jeg tilgår et interface. Bruger jeg TInterfaceList, så gør der ikke. Så humlen er, at bruge TInterfaceList når dine listeobjekter bruger interfaces. Simple as that, men der kan føjes flere detaljer på.

Må antage at fordi de gør det, så skal jeg heller ikke tænke på at frigive dem igen. Det skulle Garbage Collectoren klare. Har ikke kunne spotte (men det er ikke ligefrem let at tjekke ordentligt) noget unormalt ramforbrug.

Tankerne og løsningen er baseret på det indslag jeg fandt på Torry, at man skal være opmærksom på, at understøtter et objekt flere interfaces, så kan man komme til at gemme et af disse interfaces i en listen.

Med fare for at bryde et copyright fra swissdelphicenter.ch, så dumper jeg et indslag derfra (sorry Torry, but you don't support deep links)

{
  The Classes unit provides a class TInterfaceList which is a TList that can store
  interfaces (yes, those that are descendants of IUnknonw). If you need to store
  interfaces do not use a TList, use TInterfaceList, otherwise you will run into
  trouble.

  Here is how to do it:
}

type
  IMyInterface = interface
    procedure AMethod;
  end;

type
  TMyObject = class(TInterfacedObject, IMyInterface)
    procedure AMethod;
  end;

  {....}

var
  InterfaceList: TInterfaceList;
  MyInt: IMyInterface;

  {....}
  MyInt := TMyObject.Create;
  InterfaceList.Add(MyInt);
 
  {....}

  MyInt := IMyInterface(InterfaceList[Index]);
  MyInt.AMethod;

  {Easy, but there is a catch. The following code will crash: }

  {... declarations like above ...}
  InterfaceList.Add(TMyObject.Create);
  MyInt := IMyInterface(InterfaceList[0]);
  MyInt.AMethod; // -> Access Violation

{
  Why is that? That is because instead of storing the IMyInterface if TMyObject we
  stored its IUnknown interface in the InterfaceList. Retrieving this resulted in an
  invalid typecast of a non-IMyInterface interface to IMyInterface. The resulting
  interface pointer pointed to a IUnknown interface which simply does not have the
  AMethod method. When we tried to call this method, the code tried to get the
  corresponding method pointer from the interface's VMT and got some garbage instead.

  The following, minimally changed code works:
}

  {... declarations like above ...}
  InterfaceList.Add(IMyInterface(TMyObject.Create));
  MyInt := IMyInterface(InterfaceList[0]);
  MyInt.AMethod; // -> Access Violation
 
{
  That is, because the explicit typecast to IMyInterface before adding the TMyObject
  object to the list returned the IMyInterface interface of TMyObject rather than
  the IUnknown interface. But since IMyInterface is a descendant interface of
  IUnknown, it can still be stored in the InterfaceList.

  Confused? Yes, so was I. It took me ages to figure out what was wrong with my
  program that crashed unexpectedly. I hope this will help others to avaoid this
  problem or at least find the reason why their programs don't behave as they should.
}
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester