Delphi 2010 includes extensive support for RTTI, also known as run-time type info or reflection. Many approaches in design used to be available only in managed languages, such as C # and Java, since they require
code annotation and introspection (introspection). Now it is possible in the world of Delphi.
What is interesting in the work of RTTI is its approach to the organization of pools of objects. Delphi is a language that doesn’t use “garbage collection”, so users need to be careful and destroy objects when they are no longer needed by doing this explicitly, or by creating or using some ownership schemes (objects), such as used by TComponent- Om, where the owner (Owner) takes responsibility for the destruction of objects.
The use of information about the types is not very well combined with the ownership scheme in the style of TComponent. Usually, working with RTTI, you need to search for objects of interest to you, do something with them and continue working further. This means that many objects can be defined for verification, but not actually used. Managing the lifetime of these objects should be quite tedious, so a different approach is used: a single global RTTI pool of objects. While at least one RTTI context is active in the program, the object pool keeps all its objects up to date. When the last context goes out of scope, the objects are released.
')
Managing the pool for work uses a Delphi entry that contains a link to the interface. When any context transferred to RTTI is used for the first time, it is placed in this interface reference. It is placed there only for the first time - once, because Delphi records do not support default constructors, which also have their own problems. For example, how do you handle exceptions in the default constructor, at all points where they may occur? Creating arrays, stream-local variables, global variables, global variables in modules, temporary objects in expressions, etc. This can become disgusting, and in C ++ it sometimes becomes.
Thus, the first use creates an interface called a pool token. It acts as a kind of descriptor with a reference count indicating the global pool of objects. As long as this interface is relevant (exists), the global pool of objects will remain relevant. Even if the RTTI context is somewhere copied, the interface management logic built into Delphi, based on the principles of COM, allows you to be sure that the interface will not be deleted prematurely, the reference count will be correct. And when the RTTI context goes out of scope, or being a local variable in a function that terminates, or a field in a remote object, the reference count decreases its value. When the link count reaches zero, the pool is empty.
The biggest advantage of this approach is that the use of RTTI, in essence, should be easy and intuitive. It is only necessary in the program code to declare a variable of the appropriate type and start using it:
procedure Foo;
var
ctx: TRttiContext;
t: TRttiType;
begin
t := ctx.GetType(TypeInfo(Integer));
Writeln(t.Name);
end;
However, the flip side is that “lazy” initialization can cause an error. Imagine this scenario:
1. Library A declares an RTTI AC context
2. User code B announces RTTI context BC
3. Code B requests some RTTI objects O from BC in order to transfer them to library A
4. BC goes out of scope
5. Library A is now trying to work with O, but discovers to its surprise that the objects were deleted prematurely, even if A already has an RTTI AC context
The problem is that A never used AC, so the pool token was not created. When BC used its context, the pool began to exist, and O objects were assigned to it; but after the BC went out of scope, the objects were released.
The solution to this problem is to let library A know that it uses the long-lived RTTI context and that it expects to interact with third-party code that creates objects from its own RTTI context and sends them back, she must be sure that a pool token has been created for this long-living context . A simple way to do this is:
type
TFooManager = class
FCtx: TRttiContext;
// ...
constructor Create;
// ...
end;
constructor TFooManager.Create;
begin
FCtx.GetType(TypeInfo(Integer));
// ...
end;
This will create only the necessary minimum of RTTI objects that are needed to represent the type of System.Integer, but more importantly, it will ensure that FCtx has a pool token and leave the global RTTI pool up to date.
In future versions of Delphi, the static method TRttiContext.Create will ensure that the value returned to them receives the pool token; but so far it is not. TRttiContext.Create was originally defined to make the TRttiContext record more similar to a class for people unfamiliar with the idiom of using interfaces for automatic deterministic control over the lifetime of objects. The corresponding TRttiContext.Free method removes the pool token, and should remain the same.
Translated.by: translated by the crowdTranslation: ©
DreamerSole ,
r3code ,
debose .