This article is one chapter of my book Delphi in all its glory – For more than mere programmers, the first book of the series.
Want to see what the compiler really does with a property? Ask for its address.
Below, Obj.Prop is a read-only property that either reads the field FProp directly or calls the getter GetProp. In the last row, GetProp returns FProp. Bump has a var Integer parameter. Delphi 13 (Win32) gives these results:
| Code | Type | read FProp | read GetProp |
|---|---|---|---|
| P:= @Obj.Prop; | Integer | Compiles. P points to FProp | E2036 Variable required |
| Bump(Obj.Prop) | Integer | E2197 Constant object cannot be passed as var parameter | E2197 |
| FreeAndNil(Obj.Prop); | TOwned (a class) | Compiles. FProp becomes nil | Compiles, no warning. The object is freed, but FProp still points to it. |
So, for @ and FreeAndNil, the compiler puts the field FProp in place of a property that reads it (so, for a property of a procedural type, such as an event, @Obj.OnEvent gives the code address stored in the field, like @Obj.FEvent, not the address of the field, which is @@Obj.FEvent). The DocWiki page “Properties (Delphi)” says: “Unlike fields, properties cannot be passed as var parameters, nor can the @ operator be applied to a property.” The compiler enforces the var half in both columns, the @ half only for a getter.
Why is var refused even for a property that reads a field? The same page gives one reason for both rules: “a property doesn’t necessarily exist in memory.” Why @ still works on such a property, the documentation does not say.
The last row is the trap. FreeAndNil takes its parameter as const [ref] (we met the [ref] decorator earlier in this book, see the chapter “By constant reference”): it receives the address of the variable we pass, sets that variable to nil through a pointer cast, then frees the object.
A const parameter also accepts a plain value, such as the result of GetProp. So the compiler stores that result in a hidden variable and hands that variable to FreeAndNil. Only the hidden variable becomes nil. FProp still points to the freed object: a dangling pointer (more about them later in this book, see the chapter “Dangling references”). Embarcadero documented this trap in the RAD Studio 10.4 release notes, where FreeAndNil “can be called passing in properties or a method result” and “The nil-ed value will then be the temporary variable in the expression”; for a property that reads its field directly, though, the table shows that FProp itself becomes nil.
The demo code is the “Properties – Address and FreeAndNil” folder.
This was one chapter of a whole book
You just read one chapter of Delphi in all its glory – For more than mere programmers: almost 600 pages that take you from the first line of Delphi to classes, safe memory management and the gotchas of the language.