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;