Zoals je misschien weet heeft Delphi geen garbare collection. ARC zal snel verwijderd worden van de niet-Windows compiler. Delphi heeft dus geen structuur voor het automatisch opruimen van objecten. We hebben onderzoek gedaan om te zien of Smartpointers geschikt zijn voor het automatisch opruimen van objecten. Het concept van Smartpointers is ontleend aan C++ en is in Delphi voor het eerst geïntroduceerd door Barry Kelly. Het Spring4D framework heeft ook een implementatie van Smartpointers.
Om een voorbeeld te geven van het gebruik van Smartpointers bekijken we een aantal codes:
var s: TStringList; begin s := TStringList.Create; try DoSomething(s); finally s.Free; end;
Met Smartpointers ziet de code er zo uit:
var s: IShared<TStringlist>; begin s := Shared.Make(TStringList.Create); DoSomething(s); end;
We hebben een testprogramma geschreven om een complexer scenario te testen. Hier is de code van het testprogramma:
program TestSmartPointer; {$APPTYPE CONSOLE} uses Spring, Diagnostics, Classes, SysUtils, System.Generics.Collections; type TTestObj = class private FDescription: string; public property Description: string read FDescription write FDescription; destructor Destroy; override; end; TTestList = class(TList<IShared<TTestObj>>) public destructor Destroy; override; end; procedure Test_SmartPointer; var lTestList: IShared<TTestList>; lTestObj: IShared<TTestObj>; i: integer; begin writeln('SmartPointer test started'); lTestList := Shared.Make(TTestList.Create); for i := 1 to 10 do begin lTestObj := Shared.Make(TTestObj.Create); lTestObj.Description := i.ToString; writeln(format('TestObj with description %s added to Testlist', [lTestObj.Description])); lTestList.Add(lTestObj); end; for lTestObj in lTestList do writeln(format('Item %d of Testlist has value %s', lTestList.IndexOf(lTestObj), lTestObj.Description)); writeln('SmartPointer test finished'); end; { TTestObj } destructor TTestObj.Destroy; begin writeln(format('TestObj with description %s is destroyed', [FDescription])); inherited; end; { TTestList } destructor TTestList.Destroy; begin writeln('TTestList is destroyed'); inherited; end; begin Test_SmartPointer; readln; end.
In dit programma wordt een generieke lijst TTestList gevuld met TTestObj objecten. Let op de verklaring van de TTestList:
TTestList = class(TList<IShared<TTestObj>>)
De uitvoer van dit testprogramma staat in de onderstaande afbeelding.
Zoals je kunt zien zijn de objecten gefreed na de ‘SmartPointer-test’ regel. Dus als de Test_SmartPointer procedure klaar is, vallen de lokale variabelen buiten de scope en worden ze automatisch opgeruimd. Dit werkt goed.
We hebben ook de prestaties en het gebruikte geheugen getest. De resultaten zijn als volgt:
In de snelheidstest werden 100.000 lijsten gemaakt en elke lijst werd gevuld met 100 items. De smartpointers zijn 29,17% langzamer dan wanneer de klassieke techniek wordt gebruikt.
In de memorytest werd 1 lijst aangemaakt met 100.000 items. Wanneer de lijst volledig gevuld was, werd het geheugen van het programma gemeten. De smartpointers gebruiken 26,94% meer geheugen dan wanneer de klassieke techniek wordt gebruikt. Dit zijn aanzienlijke verschillen.
Conclusie
Smartpointers werken technisch prima. De objecten worden vrijgemaakt zoals verwacht. Er is echter een aanzienlijk nadeel als het gaat om snelheid en gebruikt geheugen. Daarnaast moet elk object worden gedeclareerd met IShared<object> en aangemaakt met Shared.Make(object.create). Onze aanbeveling van smartpointers is dat het een beetje te veel overhead in performance en code is om te worden gebruikt als een eenvoudig te gebruiken algemene stucture voor het automatisch vrijmaken van objecten in Delphi.
Referenties:
https://blog.marcocantu.com/blog/2018-october-Delphi-ARC-directions.html
http://blog.barrkel.com/2008/09/smart-pointers-in-delphi.html
https://delphisorcery.blogspot.com/2015/01/smart-pointers-in-delphi.html
https://en.delphipraxis.net/topic/402-smart-pointers-generics-vrs-non-generic-implementastion/
Contact