Last Updated:

In-memory representation of the interface | Delphi

A deep understanding of interfaces requires knowledge of their technical implementation. Therefore, you need to understand how the interface appears in the computer's RAM, and what is behind the Intf operators := Obj and Intf.NextLine.

The interface essentially acts as an additional table of virtual methods, the reference to which is laid among the fields of the object. This table is called the interface method table. It stores pointers to the methods of the class that implement the methods of the interface.

A front-end variable stores a reference to a hidden object field that contains a pointer to the interface's method table. When an interface variable is assigned a value to an object variable,

Intf := Obj; where Intf: ITextReader and Obj: TTextReader

an offset is added to the object's address to a hidden field within the object and this result is written to the interface variable. To verify this, look in the debugger for the Values of Pointer(Obj) and Pointer(Intf) immediately after executing the Intf statement := Obj. These values will be different! This is because an object reference points to the beginning of an object, and a front-end reference points to a hidden field within the object.

 

The algorithm for calling an interface method is the same as the algorithm for calling a class method. When a method is called through an interface variable,

Intf.NextLine;

the following algorithm is implemented:

  1. An address is retrieved from the interface variable (it stores the address of the interface's method table);
  2. The address of the interface method table is retrieved at the resulting address;
  3. Based on the serial number of the method in the interface, the address of the corresponding subroutine is extracted from the table;
  4. The code located at this address is called. This code transitions from an interface method to an object method. Its task is to restore the value of the Self pointer from the interface reference (by subtracting a pre-known value) and jump directly to the code of the class method.

By conventional procedural programming, this algorithm is implemented as follows:

type TMethodTable = array[0..9999] of Pointer;

TNextLineFunc = function (Self: ITextReader): Boolean;var Intf: ITextReader;

IntfPtr interface variable: Pointer; the address inside the TablePtr interface variable: ^TMethodTable;

pointer to the method table of the MethodPtr interface: Pointer; pointer to thebegin ...

IntfPtr := Pointer(Intf);

1) extracting the address from the interface variable TablePtr := Pointer(IntfPtr^);

2) retrieving the address of the method table of the MethodPtr interface := TablePtr^[3];

3) extracting the address of the desired method from the table TNextLineFunc(MethodPtr)(Intf);

4) Call the method through the adapter ... end.

All this complexity is hidden in the Delphi language behind the concept of interface. Moreover, despite such a number of operators in the example, calling a method through an interface in native code is performed very efficiently (only a few processor instructions), so in the vast majority of cases, losses per call can be neglected.