Records, memória e RTTI

No artigo anterior eu falei um pouco sobre como os Records podem ser uma mão na roda. Mas e como a memória é gerenciada com ele? Como devo trabalhar com a Rtti? Grave bem essas perguntas!

Gerenciamento de memória

Um dos fatores que tornam os Records tão práticos é o fato de, ao saírem do seu escopo, são automaticamente liberados da memória. E ao se tornarem “tipos gerenciáveis” (por favor, reparem nas aspas), ficaram ainda mais poderosos. Barry Kelly fez um pequeno experimento para exemplificar este poder (clique aqui para abrir o site dele).

Um fato que me preocupou durante as pequisas que fiz, foram algumas afirmações que o gerenciamento de memória do record não era boa. Que ao ser passado por parâmetro, era feita uma “cópia do record na memória”. Isso me deixou bastante preocupado. Se eu utilizo um record como DTO, ao transitar por entre métodos (e quem pratica o clean code escreve bastante métodos), eu teria cópias e cópias de objetos pesados em memória.

Mas a coisa não é bem assim.

Fiz alguns testes bem simples, apenas para testar se o endereço de memória do record — e de um possível objeto dentro dele — seriam alterados.

O Const, o Var e o Nada

Pessoal geralmente escreve procedimentos sem levar em consideração os modificadores de parâmetro. E não raramente, vemos métodos como:

procedure Foo(AText: String);

O caso é que, dependendo do tipo que passamos por parâmetro, a memória é gerenciada de uma forma diferente. E dependendo do modificador de parâmetro que utilizamos, a memória é gerenciada de outra forma.

Por exemplo. No caso acima, um novo espaço na memória alocado para AText. A única forma de passar a mesma string, no mesmo espaço de memória é alterando o método para:

procedure Foo(var AText: String);

Se no lugar de uma string, nós tivéssemos um Record, na alternativa sem modificador uma cópia do record realmente seria criada na memória. O que faz todo sentido em relação a semântica de um método sem modificador. Agora, se fosse incluído um modificador const ou var, neste caso não seria feita uma cópia do record!

const: A variável passada por parâmetro não poderá ser modificada dentro do método receptor. Modificada em relação ao endereço de memória que ela referencia.

var: A variável passada por parâmetro poderá sofrer alterações e essas alterações serão refletidas na própria variável. Ou seja, é possível alterar a referência na memória dentro do método receptor.

Nenhum modificador: É criada uma cópia do conteúdo da variável e todas as alterações feitas nela não refletem na origem (por isso a cópia).

Sobre a Rtti

Quando manipulamos as propriedades de uma classe via Rtti, geralmente um simples SetValue em um TRttiProperty já é suficiente.

Por outro lado, quando manipulamos tipos estruturados tudo muda. É preciso identificar a classe, criar uma instância do objeto e só então fazer a atribuição corretamente. Mas como fazer isso com Records, principalmente se estamos alterando o operador de atribuição?

Infelizmente não é possível fazer algo como:

AProperty.PropertyType.AsRecord
  .SetValue(AInstance.'Um valor string, por exemplo');

Você também não poderia fazer a codificação

AProperty.SetValue(AInstance, TValue.From<String>(‘Um valor qualquer’));

Em tempo de execução teríamos uma falha. A única forma de atribuir um a uma propriedade que seja Record é… Atribuindo um Record!

procedure SetandoString(AInstance:TObject; AProperty:TRttiProperty; AValue:TValue);
var
 _recordString:Nullable<String>;
begin
  _recordString := AValue.ToString;
  AProperty.setValue(AInstance, TValue.From<Nullable<string>>(_recordString));
end;

O grande problema dessa abordagem é a questão memória. Neste momento é feita uma cópia do record. Se o Record em si for muito pesado, essa operação pode levar tempo. E mesmo que não seja, essas operações de alocação de memória são mais demoradas que simplesmente apontar para um endereço.

A boa notícia é que, apesar de um novo record ser criado, para a cópia é passada a referencia do objeto e não uma cópia. Imagine se um desses objetos é um DataSet com milhões de registros? Se uma cópia fosse feita, teríamos uma bela demora (fora outros problemas). Mas como só a referência é repassada, ganhamos em tempo e consumo de memória!

Bom, por enquanto essas tem sido as minhas brincadeiras com o Records. Este artigo foi baseado neste aplicativo que escrevi apenas para testar as afirmações que fiz aqui. Caso você encontre algum erro, por favor, me avise!

Github do projeto em: https://github.com/ftathiago/TesteRecords

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.