Einzelnen Beitrag anzeigen

Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.063 Beiträge
 
Delphi 12 Athens
 
#13

AW: Data-Pointer in Interfacevariable casten

  Alt 15. Jul 2010, 20:13
In GetAllAccounts hat tmp._AddRef; nichts zu suchen, denn die Interfacelist arbeites ja korreckt und das Objekt wird da auch nie als Objekt verwendet.

Das Problem mit der Referenzzählung liegt an einer ganz anderen Stelle.
Und zwar bei item.Data := Pointer(tmp); , denn da wird eine Instanz des Interfaces gespeichert, aber ohne Berücksichtigung der Referenzzählung.
Nun gibt die InterfaceList natürlich beim FreeAndNil(x) ihre Referenzen frei und da es sonst keine registrierten RTeferenzen gibt, würden da die Interfaces/Objekte natürlich freigegeben.

Außerdem ist der Cast IInterface(ListViewMain.Selected.Data); falsch, denn in .Data steckt ein IAccountData und kein IInterfce.
Beide Interfaces (IAccountData und IInterfce) implementieren zum Glück das selbe QueryInterface, so daß es hier zufällig nicht zu einem Problem kommt.


Delphi-Quellcode:
type
  IDataObject = interface(IInterface)
    ['{C94DCAC7-CA42-45D3-95D7-9BC321BA2E2A}']
    procedure Delete;
  end;

  IAccountData = interface(IInterface)['{DA626905-CB35-4474-B353-9F8E973709AA}']
    function GetId: Integer;
    procedure SetId(_Id: Integer);
    property ID: Integer read Getid write SetId;
  end;

  TAccountData = class(TInterfacedObject, IAccountData)
  protected
    FID: Integer;
    function Getid: Integer;
    procedure SetId(_Id: Integer);
  public
    destructor Destroy; override;
  end;

  TAccountDataExtend = class(TAccountData, IDataObject)
  public
    procedure Delete;
  end;

procedure TForm9.ListViewMainClick(Sender: TObject);
var
  obj: IAccountData;
  obj3: IDataObject;
begin
  //inherited; // himi: nicht nötig ... oder wurde hier was überschrieben?
  if ListViewMain.ItemIndex >= 0 then begin
    obj := IAccountData(ListViewMain.Selected.Data);
    if Succeeded(obj.QueryInterface(IDataObject, obj3)) then begin
      obj3.Delete; //Funktion siehe unten
    end;
  end;
end;

procedure TForm9.ListViewMainDeletion(Sender: TObject; Item: TListItem);
begin
  // himi: korreckt, nur daß hier nun nicht mehr die
  // fehlerhafte Referenz von .GetAllAccounts freigegeben
  // wird, sondern die aus .Refresh ;)
  IAccountData(Item.Data)._Release;
end;

procedure TForm9.Refresh;
var
  tmp: IAccountData;
  i: Integer;
  item: TListItem;
  x: TInterfaceList;
begin
  // Vorgängig Listview aufräumen, Objekte zerstören, Interface Releasen
  // himi: das sollte diese Zeile erledigen
  ListViewMain.Items.Clear;

  x := TInterfaceList.Create;
  try
    GetAllAccounts(x); //Siehe Funktion unten
    for i := 0 to x.Count - 1 do begin
      if Succeeded(x[i].QueryInterface(IAccountData, tmp)) then begin
        item := ListViewMain.Items.Add;
        item.Caption := IntToStr(tmp.ID); //Zeigt korrekte ID an
        tmp._AddRef; // himi: hier die Referenz für .Data reservieren
        item.Data := Pointer(tmp);
      end;
    end;
  finally
    FreeAndNil(x);
    //Ich nehme an die InterfaceList zerstört die Objekte nicht
    // himi: doch, wenn sie die "einzige" Referenz der
    // Interfaces besitzen würde
  end;
end {LoadListView};

procedure TForm9.Button1Click(Sender: TObject);
begin
  Refresh;
end;

procedure TForm9.GetAllAccounts(_o: TInterfaceList);
var
  tmp: IAccountData; //Oder IInterface?
    // himi: nee, denn sonst könnte man ja nicht auf .ID zugreifen
  i: integer;
begin
  for i := 0 to 4 do begin
    tmp := TAccountDataExtend.Create;
    tmp.ID := i;
    //tmp._AddRef; // himi: wie gesagt ... ist falsch hier
    _o.Add(tmp);
  end;
end {GetAllAccounts};

//----------------- TAccountDataExtend

procedure TAccountDataExtend.Delete;
begin
  ShowMessage(IntToStr(FID));
end {Delete};

{ TAccountData }

destructor TAccountData.Destroy;
begin
  ShowMessage('del ' + IntToStr(FID));
  inherited;
end;

function TAccountData.GetId: Integer;
begin
  Result := FID;
end;

procedure TAccountData.SetId(Id: Integer);
begin
  FID := Id;
end;
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.

Geändert von himitsu (15. Jul 2010 um 20:18 Uhr)
  Mit Zitat antworten Zitat