2012-10-30 9 views
6

Dopo aver letto più post su StackOverflow circa gli svantaggi dell'utilizzo del conteggio di riferimento automatico per le interfacce, ho iniziato a provare a fare riferimento manualmente al conteggio di ciascuna istanza di interfaccia.Interfacce senza conteggio di riferimento

Dopo aver provato per un intero pomeriggio mi arrendo!

Perché ottengo la violazione di accesso quando chiamo FreeAndNil (p)?

Quello che segue è un elenco completo della mia unità semplice.

unit fMainForm; 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs, StdCtrls; 

type 
    TForm4 = class(TForm) 
    btn1: TButton; 
    procedure FormCreate(Sender: TObject); 
    procedure btn1Click(Sender: TObject); 
    end; 

type 
    IPersona = interface(IInterface) 
    ['{44483AA7-2A22-41E6-BA98-F3380184ACD7}'] 
    function GetNome: string; 
    procedure SetNome(const Value: string); 
    property Nome: string read GetNome write SetNome; 
    end; 

type 
    TPersona = class(TObject, IPersona) 
    strict private 
    FNome: string; 
    function GetNome: string; 
    procedure SetNome(const Value: string); 
    protected 
    function _AddRef: Integer; stdcall; 
    function _Release: Integer; stdcall; 
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; 
    public 
    constructor Create(const ANome: string); 
    destructor Destroy; override; 
    end; 

var 
    Form4: TForm4; 

implementation 

{$R *.dfm} 

procedure TForm4.FormCreate(Sender: TObject); 
begin 
    ReportMemoryLeaksOnShutdown := True; 
end; 

procedure TForm4.btn1Click(Sender: TObject); 
var 
    p: IPersona; 
begin 
    p := TPersona.Create('Fabio'); 
    try 
    ShowMessage(p.Nome); 
    finally 
    FreeAndNil(p); 
    end; 
end; 

constructor TPersona.Create(const ANome: string); 
begin 
    inherited Create; 
    FNome := ANome; 
end; 

destructor TPersona.Destroy; 
begin 
    inherited Destroy; 
end; 

function TPersona._AddRef: Integer; 
begin 
    Result := -1 
end; 

function TPersona._Release: Integer; 
begin 
    Result := -1 
end; 

function TPersona.QueryInterface(const IID: TGUID; out Obj): HResult; 
begin 
    if GetInterface(IID, Obj) then 
    Result := S_OK 
    else 
    Result := E_NOINTERFACE; 
end; 

function TPersona.GetNome: string; 
begin 
    Result := FNome; 
end; 

procedure TPersona.SetNome(const Value: string); 
begin 
    FNome := Value; 
end; 

end. 

risposta

11

La violazione di accesso si verifica perché FreeAndNil riceve un parametro var non tipizzata che dovrebbe essere il riferimento all'oggetto. Stai passando un riferimento all'interfaccia che non soddisfa il requisito. Sfortunatamente lo trovi solo in fase di esecuzione. Questo è, a mio avviso, il punto più forte contro l'uso di FreeAndNil.

Il conteggio dei riferimenti disabilita la gestione a vita mediante il meccanismo di conteggio dei riferimenti dell'interfaccia. Per distruggere un oggetto devi chiamare il suo distruttore. E per farlo devi avere accesso al distruttore. La tua interfaccia non espone il distruttore (e non dovrebbe). Quindi, possiamo dedurre che, per distruggere l'oggetto, è necessario avere un riferimento a un oggetto.

Ecco alcune opzioni:

var 
    obj: TPersona; 
    intf: IPersona; 
.... 
obj := TPersona.Create('Fabio'); 
try 
    intf := obj; 
    //do stuff with intf 
finally 
    obj.Free; 
    // or FreeAndNil(obj) if you prefer 
end; 

o si può fare in questo modo

var 
    intf: IPersona; 
.... 
intf := TPersona.Create('Fabio'); 
try 
    //do stuff with intf 
finally 
    (intf as TObject).Free; 
end; 
+0

+1 non avevo idea che si potrebbe semplicemente typecast un'interfaccia a un TObject. * (non l'ho provato, ma provenendo da te, presumo sia corretto) * –

+2

@Lieven Solo in alcune delle più recenti versioni di Delphi è supportato. O 2009 o 2010. Non riesco a ricordare esattamente quando. –

+0

David, grazie! le tue soluzioni (entrambe) funzionano come un incantesimo (come per ogni risposta che pubblichi su StackOverflow ;-)) GRANDI !!!! –

0

Non è possibile utilizzare FreeAndNil() con un riferimento di interfaccia, solo un riferimento objct. Se aveste lasciato conteggio dei riferimenti dell'interfaccia abilitato, si sarebbe sufficiente assegnare nil al riferimento di interfaccia (o semplicemente lasciarlo andare fuori del campo di applicazione) per liberare l'oggetto in modo corretto, per esempio:

type 
    TPersona = class(TInterfacedObject, IPersona) 
    strict private 
    FNome: string; 
    function GetNome: string; 
    procedure SetNome(const Value: string); 
    public 
    constructor Create(const ANome: string); 
    destructor Destroy; override; 
    end; 

procedure TForm4.btn1Click(Sender: TObject); 
var 
    p: IPersona; 
begin 
    p := TPersona.Create('Fabio'); 
    try 
    ShowMessage(p.Nome); 
    finally 
    p := nil; 
    end; 
end; 

Ma dal momento che è stato disattivato il riferimento contare sull'interfaccia, è necessario tornare ad usare le normali variabili oggetto di riferimento nel codice, ad esempio:

procedure TForm4.btn1Click(Sender: TObject); 
var 
    p: TPersona; 
    intf: IPersona; 
begin 
    p := TPersona.Create('Fabio'); 
    try 
    if Supports(p, IPersona, intf) then 
     ShowMessage(intf.Nome); 
    finally 
    FreeAndNil(p); 
    end; 
end; 
Problemi correlati