Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi interfaces and class variable types

Tags:

delphi

I have read that interfaces are a good thing to decopule the code and Nick Hodges has written a good chapter on it. Reading that I have produced this code:

//interfaces
type
 ILocalization = interface
  ['{1D144BCE-7D79-4672-8FB5-235422F712EE}']
  function localize(const aWordId: string): string;
 end;

type
 IActions = interface
  ['{31E8B24F-0B17-41BC-A9E4-F93A8E7F6ECF}']
  procedure addWord(const aIndex, aWord: string);
  procedure removeWord(const aIndex: string);
 end;

//implementation
type
 TLocalization = class sealed (TInterfacedObject, ILocalization, IActions)
  private
   FTranslationList: TDictionary<string, string>;
  public
   constructor Create;
   destructor Destroy; override;
   //interface implementations
   function localize(const aWordId: string): string;
   procedure addWord(const aIndex, aWord: string);
   procedure removeWord(const aIndex: string);
 end;

I am planning to use this class to localize (translate) my delphi android apps.


When I am going to make an instance of the class I do the following.

var
 Italiano: TLocalization;
begin
 Italiano := TLocalization.Create;
end;

Since TLocalization should have inherited AddRef and Release I won't try finally this, but does this happen even if Italiano is a class type and not an interface type?

What I mean is this:

  • Italiano: TLocalization -> here I can use all the methods
  • Italiano: ILocalization -> here I can use only the localize function

Given the fact that (as I've already said) TLocalization inherits the AddRef and Release I don't have to free if I have understood correctly. It should be the same with ILocalization but does it have other benefits? I don't understand what are the differences of the 2 cases above

like image 787
Raffaele Rossi Avatar asked Nov 24 '25 20:11

Raffaele Rossi


1 Answers

var
  Italiano: TLocalization;
begin
  Italiano := TLocalization.Create;
  // do stuff
end;

This opens you up to the pitfalls of mixing different lifetime models. As the code stands, AddRef has not been called, and so the reference count is 0. Consequently, you will leak this object.

So you might change the code to:

var
  Italiano: TLocalization;
begin
  Italiano := TLocalization.Create;
  try
    // do stuff
  finally
    Italiano.Free;
  end;
end;

Now you don't leak.

Great. But what happens if you do take a reference?

var
  Italiano: TLocalization;
  Localization: ILocalization;
begin
  Italiano := TLocalization.Create;
  try
    Localization := Italiano as ILocalization;
    // do stuff
  finally
    Italiano.Free;
  end;
end;

Now AddRef is called when you assign to Localization. So the reference count goes to 1. When Localization goes out of scope, Release is called and the reference count returns to 0 and the instance is destroyed. Unfortunately, you are also explicitly destroying it. Objects need to be destroyed exactly once.

The cleanest and simplest rule to follow is not to mix lifetime models. If you have lifetime management by reference counting, do that only. Make sure that you always take a reference to the object when you create it, and let the compiler generate reference counting code to manage the lifetime.

One way to make sure that you follow that rule is to ensure that you only ever access the object through interface references. Like this:

var
  Italiano: ILocalization;
begin
  Italiano := TLocalization.Create;
  // do stuff
end;
like image 186
David Heffernan Avatar answered Nov 27 '25 11:11

David Heffernan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!