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 ...
Комментариев нет:
Отправить комментарий