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;