Interfaces and Reference Counting

18.1 Declaring and Implementing Interfaces

type
  ILogger = interface
    ['{8F1D2A3B-1C2D-4E5F-9A8B-7C6D5E4F3A2B}']   // GUID (Ctrl+Shift+G generates one)
    procedure Log(const Msg: string);
  end;

  INotifier = interface
    ['{1A2B3C4D-5E6F-4A7B-8C9D-0E1F2A3B4C5D}']
    procedure Notify(const Recipient, Msg: string);
  end;

  TConsoleLogger = class(TInterfacedObject, ILogger)   // TInterfacedObject provides reference counting
  public
    procedure Log(const Msg: string);
  end;

  TEmailService = class(TInterfacedObject, INotifier, ILogger)
  public
    procedure Notify(const Recipient, Msg: string);
    procedure Log(const Msg: string);
  end;

procedure TConsoleLogger.Log(const Msg: string);
begin
  Writeln('[LOG] ', Msg);
end;

var Logger: ILogger := TConsoleLogger.Create;    // freed automatically when the last reference goes away
Logger.Log('Started');
// No Free needed! Never mix object references and interface references to the same TInterfacedObject.

18.2 IInterface and Reference Counting

IInterface (= IUnknown): QueryInterface, _AddRef, _Release. TInterfacedObject frees itself when the refcount reaches 0.

Hold interface-implementing objects only via interface variables.

Beware circular references (A ↔ B). Use the [weak] or [unsafe] attribute to break cycles.

Use TNoRefCountObject (or a custom _AddRef returning -1) when you want interfaces without automatic freeing (e.g., components).

type
  TChild = class(TInterfacedObject, IChild)
  private
    [weak] FParent: IParent;      // weak reference: no refcount, automatically niled
  end;

18.3 Querying and Casting Interfaces

var Svc: IInterface := TEmailService.Create;
var N: INotifier;
if Supports(Svc, INotifier, N) then N.Notify('[email protected]', 'Hi');
var L := Svc as ILogger;          // raises if not supported (requires a GUID)
var Obj := L as TEmailService;    // interface → object cast (2010+)

18.4 Interface Delegation and Method Resolution

Interfaces enable dependency injection (Spring4D container), testing with mocks (Delphi-Mocks, Spring4D mocks), plugins, and COM.

type
  TMyClass = class(TInterfacedObject, ILogger)
  private
    FLogger: ILogger;
  public
    property Logger: ILogger read FLogger implements ILogger;   // delegate the implementation
  end;

  TOther = class(TInterfacedObject, ILogger)
    procedure ILogger.Log = WriteLog;                            // method resolution clause (rename)
    procedure WriteLog(const Msg: string);
  end;