ARC for non-managed types

As most of you knows there are managed types in Delphi which memory is automatically managed – like strings, dynamic arrays, interfaces etc. And as well there are some non-managed types like Integer, Double, classes. Sure we have TInterfacedObject but it only releases automatically when you assign it to interface variable and it goes out of scope. But we also often use non-interfaced objects like TStream (all descendants), TStrings, TList<T>, TDictionary<K,V>.

It will be really nice to have them automatically managed as well, but that’s available only in ARC platforms (mobile). Can we get that in Win32/Win64? The partial answer is to use the Spring framework which introduces IList<T>, IDictionary<T> and many other good things which you can start using by adding Spring.Collections to your uses list and then constructing them through the TCollections.CreateList<T>TCollections.CreateDictionary<K,V> and other calls. But that solves the problem only with the limited list of generic types…

The idea about how to create that is not new. One version is demonstrated by Delphi guru Marco Cantu in his book. The thing is that I actually did not like the name of the class he used. I wrote my implementation based on his one which is more comfortable for me. And as well I call that record Arc (automatic reference count).

So what it does? There I show you few examples:

Example 1:

uses
  uArc;
var
  L: Arc<TList<Integer>>;
begin
  L := TList<Integer>.Create;
  L.This.Add(1); 
  L.This.Add(2); 
  // and no need to release the list anymore! 
end;

Example 2:

uses
  uArc;
var
  M: Arc<TMemoryStream>;
begin 
  M := TMemoryStream.Create;
  M.This.LoadFromFile('...');
  // and no need to release the memory stream!
end;

The full source code is available at GitHub.

5 comments

  1. Suggested implementation/examples are based on assumption than compiler will keep instance of Arc alive for some time even if it is out of the scope already (in your example scope of Arc is single line where it is used). But compiler never promised to do that and never supposed to do that. As you know – assumption is the mother of all problems. For example next test will fail:

    uses
    uArc;

    type
    TTest = class(TObject)
    public
    class var Cnt: integer;

    constructor Create;
    destructor Destroy; override;
    end;

    constructor TTest.Create; begin inc(Cnt); end;
    destructor TTest.Destroy; begin dec(Cnt); inherited; end;

    procedure TForm13.FormCreate(Sender: TObject);
    var
    A: array[0..1] of TTest;
    I: integer;
    begin
    Assert(TTest.Cnt=0);
    for I := 0 to 1 do
    A[I] := Arc.Create;

    { Now we expect that we can do something with elements of A,
    but one instance of two is dead already and next Assert will fail. }
    Assert(TTest.Cnt=2);
    for I := 0 to 1 do
    Caption := A[I].ClassName;
    end;

    Like

    1. Ok. So I adjusted the examples.. Yeah sad, but we still need that Arc variable declared in scope to ensure that it keeps the reference!
      Thank you for your finding.

      Like

  2. Yes, that will be usefull probably. Or it should be possible to disable it for a certain parts of the project (unit scope or routine scope) to make it more easy to port legacy code..

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s