acbr-components
Architectural, dependency injection and UI patterns for the ACBr Project (Commercial Automation Brazil) ecosystem in Delphi.
Good memory management practices, memory leak prevention and exception handling in Delphi
$ npx -y skills add delphicleancode/delphi-spec-kit --skill delphi-memory-exceptions --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/delphi-memory-exceptionsContext preview
The summary Claude sees to decide when to auto-load this skill.
Good memory management practices, memory leak prevention and exception handling in Delphi
name: "Delphi Memory and Exceptions" description: Good memory management practices, memory leak prevention and exception handling in Delphi
Delphi has **manual memory management** for class instances (not derived from interfaces) and uses **ARC (Automatic Reference Counting)** only for interfaces (`IInterface`), Strings, Dynamic Arrays and anonymous types. Poor exception handling and forgetting to release memory result in chronic **Memory Leaks**, catastrophic production failures and systemic instability.
Like AI, you must proactively ensure that every object you create is freed regardless of error streams.
---
Whenever an object instance is created and does not have an Owner who manages it, instantiate it in a `try..finally` block. `try` must occur **IMMEDIATELY** on the line following creation.
var
LList: TStringList;
begin
LList := TStringList.Create;
try
LList.Add('Item 1');
// ...
finally
LList.Free;
end;
end;**Anti-Pattern (DO NOT USE):** Code between `Create` and `try` may generate an exception, leaking the newly created object.
//WRONG - potential leak!
LList := TStringList.Create;
LList.Add('Item 1');
try
// ...When allocating multiple temporary resources in the same method, do not nest dozens of `try..finally` if it is not strictly necessary. But be careful to initialize them all with `nil` beforehand if there is a chance of leakage, or nest them prudently. The ideal pattern is guaranteed sequential release, but strict nesting is safest for chained allocations:
var
LStream: TMemoryStream;
LReader: TStreamReader;
begin
LStream := TMemoryStream.Create;
try
LReader := TStreamReader.Create(LStream);
try
//logics with both
finally
LReader.Free;
end;
finally
LStream.Free;
end;
end;If an API takes a class parameter, declare an interface or instantiate it before the method with `try..finally`. Never pass an inline `.Create` to a parameter in a method if you do not have an absolute guarantee that the consuming function will free the memory.
For Dependency Injection, Repository/Service Patterns or Temporary Functional Classes, use inheritance from `TInterfacedObject` linked to a `Interface`. Delphi will kill the instance when the reference counter reaches zero.
var LService: ICustomerService; begin //No try..finally and no .Free calls. //Memory is scanned when leaving the scope of this procedure. LService := TCustomerService.Create; LService.ProcessDailyBatch; end;
---
Use `try..except` primarily to trap recoverable errors, log failures without breaking loops, or transform infrastructure exceptions into more semantic domain exceptions. Never "Swallow" an exception without logical justification.
try
PerformDatabaseCommit;
except
//SPECIFIC database capture
on E: EFDDBEngineException do
begin
Logger.Error('Falha no banco de dados [Cód: %d]: %s', [E.ErrorCode, E.Message]);
raise EDatabaseConnectionException.Create('Serviço temporariamente indisponível.');
end;
//Validation SPECIFIC Capture
on E: EValidationException do
begin
ShowWarning(E.Message);
end;
end;**Anti-Pattern (DO NOT USE):** This blinds the application trace (hides `AccessViolations` and `Out of Memory`).
try ProcessData; except //Wrong! Hides any developer errors during debugging! end;
Do not use `raise Exception.Create(str)`. Declare cohesive exceptions to enable elegant interception by upper layers (REST Controllers, UI Interface).
type //Domain/Essence of the Rules EBusinessRuleException = class(Exception); ECustomerLimitReachedException = class(EBusinessRuleException); //Infrastructure EInfrastructureException = class(Exception); EDatabaseConnectionException = class(EInfrastructureException);
If you only need to perform a one-off log but want the exception to flow naturally to the global UI, just use pure `raise;`.
try
SomeDangerousCall;
except
on E: Exception do
begin
Logger.LogError('Critical failure', E);
raise; //REPROJECT the original exception with the same stack-trace
end;
end;---
When asked to write/refactor code: 1. Review whether each `TObject.Create` will result in a deallocation (`.Free` or Third-Party Ownership). 2. Inject `try..finally` if you notice legacy code without it. 3. If you generate Services, recommend Dependency Injection via Interfaces (`IService`) to simplify garbage scanning (GC). 4. In logic that may fail, create Typological Exceptions to validate flow and prevent error code spaghetti conditionals (`if return = -1 then`).
An opinionated ecosystem of rules, skills and steerings to elevate Delphi development to state-of-the-art with Artificial Intelligence.
Repo: delphicleancode/delphi-spec-kit
Architectural, dependency injection and UI patterns for the ACBr Project (Commercial Automation Brazil) ecosystem in Delphi.
Pragmatic clean code standards for Delphi — concise, direct, no over-engineering
Delphi code review checklist — quality, security, performance, SOLID, memory
SOLID implementation patterns for Delphi projects — Repository, Service, Factory, Strategy with constructor injection and interfaces
Implementation of the 23 GoF (Gang of Four) patterns in Object Pascal / Delphi with interfaces, TInterfacedObject and SOLID principles. Covers Creational,…
Standards for using DevExpress (DEXT) components in Delphi VCL applications