2015-11-23 17 views
7

Il codice seguente tenta di utilizzare la funzione di conteggio dei riferimenti di Delphi.Perdita di memoria quando si utilizza la funzione di conteggio dei riferimenti di Delphi

Tuttavia, FastMM4 nei rapporti FullDebugMode DoStuff1 fornisce perdite di memoria mentre non lo è DoStuff2. Puoi aiutare a commentare sul perché? Non dovrebbero queste due procedure comportarsi esattamente allo stesso modo dietro la scena?

program Project_SO; 

{$APPTYPE CONSOLE} 

uses 
    FastMM4, 
    SysUtils; 

type 
    ITestFunc = interface 
    ['{B3F6D9A7-FC77-40CE-9BBF-C42D7037A596}'] 
    function DoIt(X,Y: Integer): Integer; 
    end; 

    TTestFunc = class(TInterfacedObject, ITestFunc) 
    public 
    function DoIt(X,Y: Integer): Integer; 
    end; 
    TTestFuncClass = class of TTestFunc; 

{ TTestFunc } 

function TTestFunc.DoIt(X, Y: Integer): Integer; 
begin 
    Result := X + Y; 
end; 

function DoStuff1(Num1, Num2: Integer; OperationClass: TTestFuncClass): Integer; 
begin 
    Result := ITestFunc(OperationClass.Create).DoIt(Num1, Num2); 
end; 

function DoStuff2(Num1, Num2: Integer; OperationClass: TTestFuncClass): Integer; 
var I: ITestFunc; 
begin 
    I := ITestFunc(OperationClass.Create); 
    Result := I.DoIt(Num1, Num2); 
end; 

begin 
    Writeln(IntToStr(DoStuff1(3, 6, TTestFunc))); 
    Writeln(IntToStr(DoStuff2(3, 6, TTestFunc))); 
end. 
+2

Non usare un cast del genere. Usa 'I: = OperationClass.Create come ITestFunc;'. In realtà potresti semplicemente fare "I: = OperationClass.Create;", ma questo non imposta il conteggio dei riferimenti su 1, il che può causare problemi. –

+1

@Rudy Imposta il conteggio rif. A 1 quando sono assegnato a. –

+1

@Xichen La tua classe avrà bisogno di un costruttore virtuale –

risposta

5
Result := ITestFunc(OperationClass.Create).DoIt(Num1, Num2); 

Nowhere qui è un riferimento all'interfaccia presa. Viene preso un riferimento quando l'interfaccia viene assegnata a una variabile o passata come parametro per valore. In effetti, passare come parametro per valore, può essere pensato come equivalente semanticamente all'assegnazione a una variabile locale nel frame del callee.

Ma da nessuna parte in questo codice è un riferimento preso. E così, dal momento che nulla ha un riferimento all'interfaccia, non c'è alcun meccanismo che possa essere distrutto. Da qui la perdita.

var 
    I: ITestFunc; 
begin 
    I := ITestFunc(OperationClass.Create); 
    Result := I.DoIt(Num1, Num2); 
end; 

In questa variante, viene effettuato un riferimento al momento dell'assegnazione. Quando la variabile locale I lascia l'ambito, il suo conteggio di riferimento diminuisce a zero e l'oggetto di implementazione viene distrutto.

Si noti che il cast non controllato è inutile qui. Il compilatore sa perfettamente che TTestFunc implementa ITestFunc e il tuo codice dovrebbe meglio essere scritto in questo modo:

var 
    I: ITestFunc; 
begin 
    I := OperationClass.Create; 
    Result := I.DoIt(Num1, Num2); 
end; 

Come suggerito nei commenti, è possibile rimuovere la variabile locale e utilizzare un controllato as Cast:

Result := (OperationClass.Create as ITestFunc).DoIt(Num1, Num2); 

Una conseguenza dell'implementazione del cast as è che viene dichiarata una variabile locale implicita a cui è assegnata l'interfaccia. Ciò significa che il conteggio dei riferimenti viene incrementato a uno e quindi decrementato a zero quando l'ambito implicito delle foglie locali.

Infine, la classe TTestFunc dovrebbe avere un costruttore virtuale poiché si intende creare un'istanza con una meta classe.

+0

Grazie mille per il vostro aiuto! La tua risposta dice che "as-cast" utilizza una variabile locale implicita, ma "hard-cast" no. Potrebbe aiutarmi a confermare? (E dove dovrebbero le persone scoprire queste informazioni?) – SOUser

+1

Cosa intendi per conferma. Il cast non controllato non produce un locale implicito, fa il cast controllato. Non credo che nulla di tutto ciò sia documentato da nessuna parte. Considera questa risposta la prossima cosa migliore. –

Problemi correlati