Como você sabe, a Delphi não tem um coletor de lixo. O ARC logo será removido do compilador sem janelas. Portanto, a Delphi não tem estrutura para liberar objetos automaticamente. Fizemos algumas pesquisas para ver se os Smartpointers são adequados para a liberação automática de objetos. O conceito de Smartpointers é emprestado do C++ e foi introduzido pela primeira vez na Delphi por Barry Kelly. A estrutura da Spring4D também tem uma implementação de Smartpointers.
Para mostrar um exemplo do uso de Smartpointers, vamos dar uma olhada em algum código:
var s: TStringList; begin s := TStringList.Create; try DoSomething(s); finally s.Free; end;
Com os Smartpointers, o código será parecido com este:
var s: IShared<TStringlist>; begin s := Shared.Make(TStringList.Create); DoSomething(s); end;
Escrevemos um programa de teste para testar um cenário mais complexo. Aqui está o código do programa de teste:
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.
Neste programa, uma lista genérica TTestList é preenchida com objetos TTestObj. Observe a declaração da Lista TTestList:
TTestList = class(TList<IShared<TTestObj>>)
A saída deste programa de teste está na figura abaixo.
Como você pode ver, os objetos são liberados após a linha “Teste SmartPointer terminado”. Assim, quando o procedimento Test_SmartPointer é finalizado, as variáveis locais saem do escopo e são automaticamente liberadas. Isto funciona bem.
Também testamos o desempenho e a memória utilizada. Os resultados são os seguintes:
No teste de velocidade foram criadas 100.000 listas e cada lista foi preenchida com 100 itens. Os smartpointers são 29,17% mais lentos do que quando a técnica clássica é utilizada.
No teste de memória 1 a lista foi criada com 100.000 itens. Quando a lista foi totalmente preenchida, a memória utilizada pelo programa foi medida. Os smartpointers usam 26,94% mais memória do que quando a técnica clássica é usada. Isto são diferenças consideráveis.
Conclusão
Smartpointers funcionam tecnicamente bem. Os objetos são liberados conforme o esperado. No entanto, há uma desvantagem considerável quando se trata de velocidade e memória usada. Além disso, todo objeto deve ser declarado com IShared<object> e criado com Shared.Make(object.create). Nossa recomendação de smartpointers é que é um pouco demais em desempenho e código para ser usado como uma estrutura geral fácil de usar para liberar objetos automaticamente no Delphi.
References:
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/
Contato