Original in Russian: http://18delphi.blogspot.ru/2013/03/blog-post_4606.html
About containers. Table of contents
For already 15 years I’ve been thinking why Borland did not implement reference counting in base classes but created the confusing class - TInterfacedObject.
Well, I implemented. Long ago. And happily use it.
One should CLEARLY separate “reference counting” from IUnknown. The first one is a CONCEPT, the second one is ONE OF the IMPLEMENTATIONS of this concept.
I’ve organized reference counting so that you can’t write:
You can only write:
Many do not like it. But I do like. I like symmetry. Who owns the object, releases it.
In fact, there are “go-carts” for those who like “short form” and for the “fighters” for the number of lines. They have been taken from Objective-C:
Next, I’d like to tell about the implementation.
First, you should understand one simple thing: there is compiler-magic when classes are created.
From the code:
and from the code:
the following is compiled:
I.e. code for constructor is nothing but a code for initialization, and code for destructor is a code for deinitialization.
The memory for the object is allocated in NewInstance and freed in DestroyInstance. This could be found in the documentation, but who really reads it :-)
The diagrams:
The code:
And the test:
... to be continued ...
About containers. Table of contents
For already 15 years I’ve been thinking why Borland did not implement reference counting in base classes but created the confusing class - TInterfacedObject.
Well, I implemented. Long ago. And happily use it.
One should CLEARLY separate “reference counting” from IUnknown. The first one is a CONCEPT, the second one is ONE OF the IMPLEMENTATIONS of this concept.
I’ve organized reference counting so that you can’t write:
List.Add(TItem.Create);
You can only write:
Item := TItem.Create; // the number of references is 1 try List.Add(Item); // the number of references is 2 finally FreeAndNil(Item); // the number of references is 1 end;
Many do not like it. But I do like. I like symmetry. Who owns the object, releases it.
In fact, there are “go-carts” for those who like “short form” and for the “fighters” for the number of lines. They have been taken from Objective-C:
List.Add(TItem.Create.Autorelease);
Next, I’d like to tell about the implementation.
First, you should understand one simple thing: there is compiler-magic when classes are created.
From the code:
A := TMyClass.Create;something like this is compiled:
A := TMyClass.NewInstance; A.Create;
and from the code:
A.Destroy;
the following is compiled:
A.Destroy; A.DestroyInstance
I.e. code for constructor is nothing but a code for initialization, and code for destructor is a code for deinitialization.
The memory for the object is allocated in NewInstance and freed in DestroyInstance. This could be found in the documentation, but who really reads it :-)
The diagrams:
The code:
RefCountedPrim.imp.pas:
{$IfNDef RefCountedPrim_imp}
{$Define RefCountedPrim_imp}
_RefCountedPrim_ = {mixin} class(_RefCountedPrim_Parent_)
private
// private fields
f_RefCount : Integer;
{* The field for the properties of RefCount}
protected
// overridden protected methods
procedure FreeInstance; override;
public
// overridden public methods
destructor Destroy; override;
class function NewInstance: TObject; override;
protected
// protected methods
procedure Cleanup; virtual;
{* The function of object fields cleaning. }
public
// public methods
function Use: Pointer;
{* increment the reference counter by 1 and return the pointer to itself. }
function SetRefTo(var F): Boolean;
public
// public properties
property RefCount: Integer
read f_RefCount;
end;//_RefCountedPrim_
{$Else RefCountedPrim_imp}
// start class _RefCountedPrim_
procedure _RefCountedPrim_.Cleanup;
begin
end;//_RefCountedPrim_.Cleanup
function _RefCountedPrim_.Use: Pointer;
begin
if (Self <> nil) then
InterlockedIncrement(f_RefCount);
Result := Self;
end;//_RefCountedPrim_.Use
function _RefCountedPrim_.SetRefTo(var F): Boolean;
begin
if (Pointer(F) = Self) then
Result := false
else
begin
Result := true;
TObject(F).Free;
Pointer(F) := Self.Use;
end;//Pointer(F) = V
end;//_RefCountedPrim_.SetRefTo
destructor _RefCountedPrim_.Destroy;
begin
if (InterlockedDecrement(f_RefCount) = 0) then
begin
Inc(f_RefCount);
try
try
Cleanup;
finally
inherited Destroy;
end;//try..finally
finally
Dec(f_RefCount);
end;{try..finally}
end;//InterlockedDecrement(f_RefCount) = 0
end;//_RefCountedPrim_.Destroy
class function _RefCountedPrim_.NewInstance: TObject;
begin
Result := inherited NewInstance;
_RefCounted_(Result).Use;
end;//_RefCountedPrim_.NewInstance
procedure _RefCountedPrim_.FreeInstance;
begin
if (f_RefCount = 0) then
inherited FreeInstance;
end;//_RefCountedPrim_.FreeInstance
{$EndIf RefCountedPrim_imp}
--------------------------------
RefCounted.imp.pas:
{$IfNDef RefCounted_imp}
{$Define RefCounted_imp}
_RefCountedPrim_Parent_ = _RefCounted_Parent_;
{$Include RefCountedPrim.imp.pas}
_RefCounted_ = {mixin} class(_RefCountedPrim_)
protected
// protected methods
destructor Destroy;
{* To prevent being lured in overriding destroy. }
end;//_RefCounted_
{$Else RefCounted_imp}
type _RefCountedPrim_R_ = _RefCounted_;
{$Include RefCountedPrim.imp.pas}
// start class _RefCounted_
destructor _RefCounted_.Destroy;
begin
assert(false, 'We are not supposed to get here');
inherited;
end;//_RefCounted_.Destroy
{$EndIf RefCounted_imp}
-------------------------------
Refcounted.pas:
unit Refcounted;
interface
type
_RefCounted_Parent_ = TObject;
{$Include RefCounted.imp.pas}
TRefcounted = class(_RefCounted_)
end;//TRefcounted
implementation
uses
Windows
;
{$Include RefCounted.imp.pas}
end.
And the test:
RefcountedTest.pas: unit RefcountedTest; interface uses BaseTest ; type TRefcountedTest = class(TBaseTest) published // published methods procedure DoIt; end;//TRefcountedTest implementation uses Refcounted, SysUtils, TestFrameWork ; // start class TRefcountedTest procedure TRefcountedTest.DoIt; var l_A : TRefcounted; l_B : TRefcounted; begin l_A := TRefcounted.Create; try Check(l_A.RefCount = 1); l_B := l_A.Use; try Check(l_A.RefCount = 2); Check(l_B.RefCount = 2); finally FreeAndNil(l_B); end;//try..finally Check(l_A.RefCount = 1); finally FreeAndNil(l_A); end;//try..finally end;//TRefcountedTest.DoIt initialization TestFramework.RegisterTest(TRefcountedTest.Suite); end.
... to be continued ...



Комментариев нет:
Отправить комментарий