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