Я считаю, что нужно сделать перерыв между статьями, для того, что бы объяснить, почему я вызываю TRttiContext.Create() и TRttiContext.Free(), хотя фактически можно этого не делать.
Да, вам не обязательно это делать, хотя я это делаю… Почему?
Прежде всего, давайте посмотрим на обе реализации.
class function TRttiContext.Create: TRttiContext;
begin
Result.FContextToken := nil;
end;
procedure TRttiContext.Free;
begin
FContextToken := nil;
end;
На первый взгляд здесь нет ничего специфического, значение FContextToken установлено NIL в обоих случаях.
Однако, что же делает установка NIL для FContextToken фактически?
Мы все прекрасно знаем, что Delphi на текущий момент не имеет никакого сборщика мусора (Специальный код, называемый сборщиком мусора (garbage collector), периодически освобождает память, удаляя объекты, которые уже не будут востребованы приложением - то есть производит сборку мусора, прим. переводчика). Следовательно, наличие различных RTTI библиотек, которые основываются на Объектах, может быть проблематичным.
В своем коде я имею возможность делать такие крутые вещи как
c.GetType().GetField().FieldType.ToString
Без необходимости установки временных значений для каждого возвращаемого объекта, что бы высвободить его.
Как это возможно без сборщика мусора?
"Под капотом" вы найдете TRttiPool, который содержит все RTTI объекты, которые уже созданы.
Когда этот пул высвобождается, все созданные в процессе вызовов RTTI объекты также высвобождаются.
Создание и уничтожение в этом пуле контролируется TPoolToken , являющимся Интерфейсом, который хранится в FContextToken.
Когда TPoolToken создан и высвобожден, обеспечивается поддержка PoolRefCount. И когда PoolRefCount принимает нулевое значение, TRttiPool высвобождается.
И так, почему вызывается .Create() и .Free()?
Давайте начнем с .Create()
Это не призыв вызывать данный код, связанный с дополнительными усилиями. Да, несомненно, запись управляема и, как таковая она будет инициализирована, но при этом FContextToken будет содержать избыточность. Давайте разберемся… Хотя я не собираюсь каждый раз создавать указатель на TRttiContext, такой подход дает возможность не занимать предполагаемый объем памяти до тех пор, пока вы не вызовете .Create.
Я так же не могу быть уверен, что реализация будет оставаться простой. Очень вероятно, что здесь будет вызываться дополнительный код. Сохраните мой код для себя, и в будущем вы сможете раскритиковать мой подход.
Переходим к .Free()
Я хочу оставить свое размещение памяти (memory foot print) чистым. Несомненно, когда переменная TRttiContext выходит за рамки обычного поведения это произойдет.
В самом начале своих опытов с RTTI, я ненароком получил access violation в деструкторе наследника TCustomAttribute. При вызове .Free() было легче увидеть, в чем заключалась проблема, чем при получении этого события после деструкии объекта, в котором TRttiContext был объявлен как поле.
И что бы отсечь тривиальные комментарии… Да, я знаю, что документация гласит, что вам не нужно вызывать .Free(). Так же предполагается, что данная технология важна, потому что она обеспечивает то, что RTTI объекты являются кэшируются и используются повторно, это будет справедливо для TRttiContext, описанного как глобальная переменная, что мне кажется очень плохой идеей.
Я думаю, что вместо этого вам следует хранить TRttiContext до тех пор, пока вам это нужно, но высвобождать его в корректное время.
Вкратце… Я считаю, что это личное дело каждого.