Posts

Pass the Dog, Get the Cat

This story begins with the FreeAndNil procedure, why its signature could not have a typed var parameter, why we can only pass variables declared as TObject to such procedures, and why the compiler refuses to compile if we try passing any other variable type even if it is a descendant of TObject.

procedure FreeAndNil(var Obj); So why does the compiler enforce this behavior? If we take a look at the FreeAndNil implementation and imagine it with a typed var parameter, it may seem that the compiler is throwing us curve balls for nothing. There is nothing we do inside that procedure that would warrant a compiler error for passing TObject descendant types:
assigning variable to another TObject variable - passassigning nil to original variable - passfreeing original object instance stored in temporary variable - pass procedure FreeAndNil(var Obj: TObject); var Temp: TObject; begin Temp := TObject(Obj); Pointer(Obj) := nil; Temp.Free; end; var Obj: TSomeObject; ... FreeAndNil(Obj…

Why some people cannot use dark themes?

Magic behind FreeAndNil

Delphi 10.4 Sydney brings one small but rather significant change. A change in the signature of the FreeAndNil procedure from
procedure FreeAndNil(var Obj); inline; to
procedure FreeAndNil(const [ref] Obj: TObject); inline; Wait, const means the variable is passed as a constant and cannot be changed from within the procedure. But the whole point of FreeAndNil is to change the passed object variable to nil. What kind of sorcery is this?

Let's start from the beginning and the old FreeAndNil procedure, its purpose and implementation.
The purpose of FreeAndNil is to simplify the following code sequence:
Foo.Free; Foo := nil;
While the actual implementation of FreeAndNil is slightly different, and it will first nil the reference and then Free the object instance (temporarily preserved in a local variable), that slight difference is not relevant for discussing the signature of FreeAndNil.

procedure FreeAndNil(var Obj); var Temp: TObject; begin Temp := TObject(Obj); Pointer(Ob…

Delphi Nullable with Custom Managed Records

Image
Published with special permission from Embarcadero - this post writes about pre-release Delphi version where everything is subject to change until finally released.



Nullables are a rather simple concept. All you need is a value, and some flag that will tell you whether or not the value has been explicitly set or not.
type TNullable<T> = record private FHasValue: boolean; FValue: T; function GetValue: T; procedure SetValue(AValue: T); public property HasValue: boolean read FHasValue; property Value: T read GetValue write SetValue; end; function TNullable<T>.GetValue: T; begin if FHasValue then Result := FValue else raise Exception.Create('Invalid operation, Nullable type has no value'); end; procedure TNullable<T>.SetValue(AValue: T); begin FHasValue := True; FValue := AValue; end;
Plain and simple. But then you have the fact that records are not automatically initialized and that your FHasValue boolean can ho…

Listen to the Memory Manager

Let's say you have a nice, working Delphi application. One fine day, for no reason whatsoever, you decide to run your fine and bug free application with FastMM in full debug mode. Just for fun... and suddenly, out of the blue, the app starts crashing with an AV reading of address 0x80808088. What is going on... ????

When a seemingly working application suddenly starts misbehaving, the first instinct would be to look at the latest code change in searching for clues. In the above case, the only change was turning on FastMM debug mode. Could this be a bug in FastMM?

While all software has bugs and it is perfectly possible that FastMM has bugs, too, it is highly unlikely that this is FastMM's fault.

If turning on the CatchUseOfFreedInterfaces define in FastMM configuration shows an attempt to use an interface on a freed object, you have found the culprit. It may require further debugging and inspecting the code, but at least you will know what to look for.

One of the "problem…

Unified Memory Management - Coming with 10.4 Beta

As announced on previous roadmaps, the 10.4 release of Delphi is planned to have unified memory management. That means retiring the ARC compiler on mobile platforms, which will now operate under the same good old manual memory management model that originated on Windows.

Unification is good. Completely different memory management models require slightly different code. That alone breaks the promise of a single code base for all platforms. In reality, if you need to target platforms with different memory management models you will find yourself coding under the requirements of manual memory management where ARC will only throw you some curveballs. Constrained ARC is bad ARC, it is not useful, it cannot unleash its full power.

On one side I am happy because of unification, on the other I am sad to see the ARC compiler go. It had great potential and was mostly brought down by unoptimized RTL/FMX code that was written for manual memory management. There is a whole lot of code that could b…

Self destructing object instance

Using reference counting classes always requires some caution. You have to pay attention to reference cycles, using interface reference for referencing such object instances, not calling FreeAndNil and more... it is quite a list...

But every once in a while, new ways of shooting yourself in the foot keep popping up... So here goes the story...

I have two reference counted classes that hold reference to each other instances. One of those references is marked as [weak] to prevent creating strong reference cycle.

type TFoo = class(TInterfacedObject) private [weak] FRef: IInterface; public constructor Create(const ARef: IInterface); end; TBar = class(TInterfacedObject) private FFoo: IInterface; public constructor Create; virtual; destructor Destroy; override; procedure AfterConstruction; override; end; constructor TFoo.Create(const ARef: IInterface); begin inheri…