Download as pdf or txt
Download as pdf or txt
You are on page 1of 370

Title

Page
Dalija Prasnikar

Neven Prasnikar Jr.

Delphi Memory Management


for Classic and ARC Compilers

Copyright © 2017 Dalija Prasnikar & Neven Prasnikar Jr.

First Edition
Introduction
Memory management. One of the most basic parts of software
development, often kept on the side even though it has the most
profound effect on how we write our code.

Delphi provides a variety of types with their own memory management


logic, as well as two sets of compilers that provide different memory
management systems for classes:

The classic Delphi compiler, currently supported on Windows and


OSX platforms - using manual memory management while providing
ARC for certain types.

The next-generation ARC Delphi compiler, supported on the mobile


Android and iOS platforms, as well as Linux, using a full ARC -
Automatic Reference Counting - memory management system.

Each memory management system has its good and bad sides. Each of
them offers solutions to some problems, but creates a whole range of
other problems, and each of them requires slightly different coding
patterns and practices. Understanding how memory management
systems work and knowing their strengths and weaknesses goes hand-
in-hand with writing clean, bug-free and maintainable code.

Both compilers will be covered in detail, along with the coding patterns
required for writing cross-compiler code that can run under both.

From manual memory management to garbage collection, different


memory management systems differ not only by the general category
into which they fall, but also by their implementations. And all those fine
implementation details also have a great impact on the actual code. From
the perspective of the everyday software development process,
discussing memory management is impossible without discussing its
specific implementation in specific languages and tool-sets.
Memory management is a fairly broad topic. There is a huge number of
combinations in behavior, and at times, just a slight modification in
otherwise working code can have devastating results. Another problem is
a difference in observed behavior, and actual implementation details that
don't actually guarantee its consistency. As a result, applications may
experience random behavior - in other words, applications may run
seemingly perfectly and only occasionally break. The lack of a Delphi -
Object Pascal programming language specification does not help, either.
Sometimes there is a thin line between intentional and accidental
behavior - that cannot be counted on. Even experienced developers
might have holes in their knowledge about specific aspects.

Managing memory is both a trivial and a complex task. Trivial, because


the basic rules are rather simple and straightforward. Managing a few
references, when you know the rules, is a piece of cake. The complex
part comes from several things. First, even though each rule is simple,
there are many of them, and knowing and remembering them all is a
challenge. Next, combining the rules makes even more rules. Some
combinations work, some don't, and while this knowledge can be
extrapolated from each single rule, it is easy to overlook some important
fact that can lead to disaster. And at last, complexity arises from all the
pieces of code sitting together in one place, obscuring each other.

What is important to remember is that managing memory requires


following the same simple principles, regardless of the size of the
codebase. Divide and conquer is the winning strategy. If each small, self-
contained piece of code, where managing memory is trivial, is done right,
then all the pieces can work together in harmony.

Another important thing to remember is that even the most competent


developers can make mistakes, including trivial ones. Manual memory
management is a particularly error prone process. It is not always a
question of whether we are capable of managing memory on our own,
but should we? There are different coding patterns and techniques that
can assist us. If using any of those fits into a particular situation, there is
no reason why we should not use them. The only important part is
making sure that by solving one particular problem, we don't leave the
doors wide open for others to come.
There is more to managing memory than handling object instances, but
that is the most complex and defining part of any memory management
system, and the core content of this book. It would be impossible to cover
the handling of object instances without knowing at least some basic
facts about the memory management of other types. The first part will
cover those basics, as well as general terminology, and the rest of the
book will focus on object instances.

The intention is to provide a reference for broad usage, covering topics


that are of common interest for most developers without going into some
specialized and more advanced areas that are rarely used. I will tend to
simplify explanations about what is going on under the hood and keep my
focus on information that can help in understanding the memory
management process without going into boring details that are of very
little relevance for writing actual code.

This book assumes anyone reading has some minimal knowledge of


general programming terminology, as well as some basic knowledge of
Delphi syntax. If you are just getting started and still need to get
acquainted, the Delphi Language Guide is a good place to start.

Acknowledgements
Huge thanks to the following engineers for providing the technical
reviews: William Meyer, David Erbas-White and Joe C. Hecht.

Thanks to Stefan Glienke for disposing of my doubts, and there have


been many. Also for keeping my sanity by confirming that my imaginary
issues can be spotted in real life.

And last but not least, huge thanks to Allen Bauer and Barry Kelly for
sharing their vast knowledge of the implementation details of the Delphi
compilers. Without that, this book would not be possible.

Code
The primary purpose of the code examples provided in this book is to
explain certain concepts, show possible mistakes and bugs, and provide
basic templates for implementing coding patterns related to memory
management.

Some more complex code examples centered around specific concepts


like SmartPointers, Lazy, Weak, Nullable and similar library-like
templates, while usable as-is, should be taken more as a starting point
rather than a complete solution. You can take that code, enhance it and
adapt it for your own needs, or even better, use the excellent Spring4D
library instead. It will not only give you feature-rich implementations of the
mentioned concepts, but also some other core types, classes and
frameworks.

Spring4D - https://bitbucket.org/sglienke/spring4d
Clean Code

Use plenty of copy-pasta code and you'll eventually end up in deep


sauce.

Talking about Clean Code in any programming book seems almost


inevitable. And with good reason. Good programming practices and
common patterns in any language make code easier to read and
understand. Also, errors of any kind (including memory issues) are more
easily detected in clean, well-organized code.

Knowing how to apply general object-oriented programming principles, as


well as writing good and clean code, are an art in themselves. Ultimately,
it is an art worth mastering. Numerous books have been written on the
topic and this short chapter is not meant to answer all the hows but to
answer at least some whys and provide some basic guidelines and
starting points for further research. Because, you cannot properly
manage memory in total chaos.

Code is read more times than it is written. Any kind of code where intent
is not clear and obvious will be inherently harder to read and therefore
more costly to maintain.

You may say, as long as it works it doesn't matter what kind of code it is.
But there is a world of difference between working code and good code.
Working code is when you have to squeeze a square peg into a round
hole and you manage to patch something together. It can certainly take
you home, but it is only a temporary solution nonetheless. You would not
want to rely on such a patch for long. What you actually want to have, is
a solution where a square peg goes into a square hole or a round peg
goes into a round hole. Anything else will only take you so far, if even.

There is another notable difference between working code and good


code in terms of memory management. Usually, memory issues come
from deeper issues with structuring and designing code from the ground
up. By the time one has to debug memory issues, it is usually way too
late. Debugging is the most expensive part of the code writing process.
Not only it is expensive, but when an error is actually found, fixing it -
beyond some quick and dirty square peg-round hole patch - falls back to
knowing how to properly structure, design and write clean, good code.

Don't let rules rule you - some rules about rules


Never say never - Following rules and guidelines is meant to help us write
better, cleaner, more maintainable code and also write it fast. At the point
where the rule no longer satisfies that purpose as a whole and becomes a
burden, it is no longer useful - and this is the point where we can make an
exception and break the rule. Making the exception does not imply that rule
in general is wrong, just that it is inappropriate in a particular situation. If
you are breaking the rules too often, it is quite possible that your
understanding of the particular rule is flawed.
Don't take rules too literally - Catchy phrases are just easily memorable
words to remind you of more elaborate principles. If you take them too
literally, you can completely miss the point behind the phrase.

Top rules that come above all else


If you find yourself in a position where two or more principles collide and
seemingly contradict each other, use the following rules as essential
guidelines for achieving proper balance:

Optimizations of performance critical code - Sometimes fast is


not fast enough. In such cases, performance requirements outweigh
all other considerations. When and if all other options for speed
improvements are exhausted, optimizing identified bottlenecks by
breaking rules and common coding patterns, and writing clever or
fragile code, is a perfectly acceptable course of action. Since
optimizations and performance reasons for writing specific code that
violates common practices are not always obvious, it is mandatory to
document them as such. But before you start messing up your code
for optimization reasons please do check and double check that you
are actually making real improvements in the right places. If the
gains are insignificant, violating good coding practices is never
justified.

KISS - Keep it simple stupid

https://en.wikipedia.org/wiki/KISS_principle

YAGNI - You aren't gonna need it

https://en.wikipedia.org/wiki/You_aren't_gonna_need_it

DRY - Don't repeat yourself

https://en.wikipedia.org/wiki/Don't_repeat_yourself

Object oriented programming guidelines


SOLID - https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)
Single Responsibility Principle
Open/Closed Principle
Liskov Substitution Principle
Interface Segregation Principle
Dependency Inversion Principle

Law of Demeter - Loose coupling and encapsulation

GRASP - General responsibility assignment software patterns

https://en.wikipedia.org/wiki/GRASP_(object-oriented_design)

Some say that SOLID is in contradiction with YAGNI and KISS. On the
contrary, they complement each other. YAGNI is about not adding
features until you really need them and not how to implement those
features. KISS is about how to keep designs simple and not over
engineering features and SOLID and GRASP are about how to write well
organized extendable and testable code, so when there is a need for
additional features they can be added with ease without breaking a whole
a lot of code.
General coding guidelines
Avoid clever code and use common coding patterns - clear code is preferred
Document unusual code and reasons behind it, where a seemingly
innocuous change might break it
Avoid fragile code - code that can easily be broken by subsequent changes
or changes in unrelated code
Avoid long methods, procedures, functions
Avoid coding patterns from other languages that don't fit well
Choose names carefully
Be consistent

Avoid clever code and use common coding patterns - clear code is
preferred

This is a common pitfall for fresh developers. Knowledge is power,


gaining too much power too fast can make you want to show it off. That is
fine. But, software development is a highly collaborative business. Even if
you are a single developer, your future self will have to collaborate with
your past self. And trust me, you will hate your smart-ass self more than
you could ever imagined.

Nobody wants clever code in their code-bases. They want code that is
easy to understand and maintain. Code that can be read and understood
by many, not just by a single person.

Leave your smart code for T-shirts and social gatherings, and use clear
code in real life. Your future self will thank you.

Document unusual code and the reasons behind it, where a


seemingly innocuous change might break it

This is an extension of the previous rule. If smart code, or any other


unusual code, solves some particular problem that cannot be done in any
other way, then by all means use it. But, you must also properly
document it. Document reasons behind the code, document why it was
necessary, document why regular code would not work, document what
code does and how.
Proper documentation, will not only save the time needed for
understanding the code, but it will also prevent accidental refactoring that
might break the code and defy its original purpose.

Avoid fragile code - code that can be easily broken by subsequent


changes or changes in unrelated code

Sometimes there is a thin line between smart/unusual/fragile code. All


combinations are possible. It is prudent to document such code and
cover it by tests.

The major difference and what really makes some code fragile, is not that
it can be broken by changes in surrounding code - any code can, for that
matter - but that it can be broken by changes in some seemingly
unrelated code far, far away.

Documenting such fragile code is of very little help. Tests can help
discover a sudden failure, but the best course of action would be avoiding
such coding patterns as much as possible.

One of the common examples in Delphi is the with construct, a relic from
the procedural programming era, that in object-oriented programming
does more harm than good. A new property, method, or a simple rename
refactoring can easily bring name collisions and different interpretation of
the code by the compiler.

Another example, covered in the DisposeOf chapter, is manually resolving


cycles for TComponent descendant object instances instead of calling
DisposeOf . TComponent classes are designed in such a way that strong
reference cycles can be created in code beyond our control and
knowledge.

A simple Delphi update, with some extended functionality, could easily


add some new functionality and break our carefully crafted code.

Avoid long methods/procedures/functions

Avoid writing long blocks of code. Code is read in blocks - the longer the
block, the more places there are for bugs to hide. Short, simple code is
more readable and easily understandable than long monolithic code.
When possible, long blocks (methods) should be broken down into
smaller logical parts.

One common exception to the above rule is implementing math


algorithms that cannot easily be broken into smaller logical pieces.

Avoid coding patterns from other languages that don't fit well

Some coding patterns have evolved from and around particular


language-specific behaviors. Trying to force such patterns into other
languages can result not only in inefficient code, but also code that is
harder to understand and maintain. This can also lead to
misunderstanding some design patterns that are solving coding problems
in one language that might not exist in other. Or the optimal solution to
the problem in one language greatly differs from the optimal solution in
another language.

For instance, probably one of the most misinterpreted concepts is


encapsulation. This is mostly due to the fact that Java does not have
properties and is one of the primary languages used for teaching OOP.

Badly explained, or simply misinterpreted by newbies, encapsulation is


often mistaken with having getter and setter methods.

This leads to blindly applying a misunderstood concept in languages that


do have properties, as one of the tools for achieving encapsulation, and
sprinkling code with completely unnecessary getters and setters.

Simply put, encapsulation is the principle of defining and achieving a


stable public API by protecting class internals that are subject to change,
as well as protecting direct access to the class members that could put
an object instance in an invalid state. The class is at liberty to evolve and
be extended, and those changes and extensions will not break consumer
code.

For instance, if we have some visual control that can be shown or hidden,
we might define that functionality with two public methods Show and Hide
and a boolean field Visible that represents the current state.
type
TControl = class(TObject)
public
Visible: boolean;
procedure Show;
procedure Hide;
end;

procedure TControl.Show;
begin
Visible := true;
end;

procedure TControl.Hide;
begin
Visible := false;
end;

And then we can show the control either by calling the Show method or by
updating the Visible field.
var
Control: TControl;
...
Control.Show;
...
Control.Visible := true;

The problem with our Visible field is not the fact that it is publicly directly
accessible, but the fact we might want to introduce some changes in the
future. For instance, we might want to add some additional logic in our
Show method. But such a change would break any consumer code that
uses the Visible field directly.
procedure TControl.Show;
begin
Visible := true;
// some additional code that must be executed when control is shown
end;

In Delphi that problem can be easily solved by declaring Visible as a


property and adding a setter that calls the appropriate code.
type
TControl = class(TObject)
protected
FVisible: boolean;
procedure SetVisible(Value: boolean);
public
procedure Show;
procedure Hide;
property Visible: boolean read FVisible write SetVisible;
end;

procedure TControl.SetVisible(Value: boolean);


begin
FVisible := true;
// some additional code that must be executed when control is shown
end;

procedure TControl.Show;
begin
Visible := true;
end;

We were able to change the class' behavior while maintaining the same
public API, without breaking consumer code. We can still change the
control's visibility by either using the Visible property or calling the Show
method.

But what happens in the equivalent Java code?

Since Java does not have properties, the only way to change behavior is
removing direct access to the Visible field. That would break any
consumer code that uses Visible directly.

To avoid breaking public APIs, Java has a general coding practice of


using getters and setters for accessing any publicly available fields. Since
most of the getters and setters are plain value retrievals or value
assignments, newbie developers tend to get confused, not grasping that
mandatory getters and setters are a mere side effect of the language
flaws, not part of encapsulation as a principle itself.

Just like forcing getters and setters in Delphi would be conceptually


wrong, there are other coding patterns that don't fit well or cannot be
used or implemented in the same way they are in some other languages.

There is nothing inherently wrong with broadening your horizons and


applying good practices learned in other languages. Not by using them
blindly, but appropriately.

Choose names carefully

There are only two hard things in Computer Science: cache


invalidation and naming things. --- Phil Karlton

Clean code is all about readability and subsequently understanding what


the code does. Nothing can convey a purpose better than a proper name.
Just like nothing can obscure the code more than a badly chosen name.

If you have ever had the pleasure of reading code that has been run
through some obfuscator, you know what I am talking about.

A rose by any other name would smell as sweet. --- William


Shakespeare

Similarly, badly named code would run just the same as properly named
code. While the compiler does not care about how good your names are,
humans do. And for better or for worse, they are still responsible for
writing, reading and understanding the code. Writing code requires more
than sticking your nose in a rose. You have to know it is a rose
beforehand, or you might get a nasty surprise.

Choose your names carefully, and make sure they clearly represent the
intent of the code behind them.

Be consistent

Whatever you do, be consistent. Consistency brings order out of chaos.


The only thing worse than bad code is inconsistent code. Even if you
follow some bad coding and naming practices, follow them consistently.

However, if you get to the point where consistency would hurt changes
and improvements, then improve the code by all means.

Document your code


And last but not least, document the code, beyond the previously
mentioned must document situations.

The most important thing to remember is that documenting the Why is


way more important than documenting the What. What code does is
usually self-evident - the reasoning behind a particular chunk of code, not
so much. That does not mean documenting the What is useless, but
when documenting the What you should document what the code should
be doing. This can help find discrepancies between the expected and
actual behaviors.

Another important thing is to keep code and documentation in sync. If


you change the code, you have to update the documentation accordingly.
Obsolete documentation is worse than none. But, that fact is not exactly
a valid excuse for not documenting code.

Don't wait too long. The best time for writing documentation is writing it
along with the code, while it is still fresh in your head. You will be able to
write it much faster and more accurately.

RTFM - Read the frickin' manual


More often than not, you will be in a situation where you have to use
code you have not written yourself. Before you jump in and start using
any unfamiliar library, framework or any other code, take some time and
read the documentation. If there is no documentation, then read the
code. If you don't understand the code, or there is no code available,
then don't use it unless absolutely necessary. Write unit tests using
possible and impossible values to see how it behaves. Those unit tests
will also serve as an indicator that the library's behavior has changed
when you upgrade to a new version.

Any trouble you will have if you don't invest some time learning how to
use the code in question, will be solely self-inflicted. Even if you do spend
time learning, there will be things you might have missed and there may
be situations where not everything will work as expected, but way, way
less frequently than if you don't bother with reading at all.

If you think you don't have time to learn, think again. You will lose way
more time debugging later on. If you still think you don't have time, I can
only say, just RTFM.

This is just a short summary of the practices and principles followed in


this book. Practical examples throughout the covered topics will show
how certain code either breaks or follows those rules. Since memory
management topics are closely related, many examples cover more than
one point and will be repeated if appropriate.
1 Short overview of memory
management models

Choosing between memory management systems is actually more


like "Pick your poison".

In general, there are two memory management models - manual memory


management and automatic memory management. The following are
some rather rough definitions of the most commonly used systems, just
enough to provide some insights into the general differences between
them, as well as the main advantages and disadvantages of each one.
The problem with different memory management systems is that none of
them are a perfect fit for all purposes, and generally coding patterns
suitable for one don't work well for the others. While some languages
strictly use a single memory management system, some use a mixture.
There are also significant differences between implementations in
different languages, platforms and toolsets. The implementation of ARC
in Delphi differs from ARC in Objective-C and Swift. The Java Virtual
Machine garbage collector is different from the one in .NET. Developers'
preferences for any particular memory management system are highly
influenced by the kind of applications they write and for which platforms.

1.1 Manual memory management


Just as its name says, in manual memory management systems
managing memory is a manual process. The developer is responsible for
writing specific code for releasing allocated memory when it is no longer
needed. Having manual memory management in place does not imply a
total lack of automatic memory management. It is possible to have a
mixture of both. Besides regular manual management of object
instances, the classic Delphi compiler also allows automatic memory
management through a reference counting mechanism using interface-
based references to object instances.

The main advantage of manual memory management is its speed. There


is no overhead in creating and destroying object instances and it gives
developers full control over the process.

Since managing objects is a manual process, it is also quite error prone.


Besides the memory leaks that will occur if the developer fails to release
memory, there is a whole range of serious errors that can be caused by
accessing invalid memory - from memory corruption to crashes and
random behavior. Some of the issues also present security
vulnerabilities. Micromanaging memory requires a lot of boilerplate code,
which obscures the real intent of the code. Some patterns such as
shared object instances, easily achieved under automatic memory
management systems, require writing rather complex code under manual
memory management.

1.2 Automatic Reference Counting - ARC


ARC stands for 'Automatic Reference Counting'. It is a form of automatic
memory management system where the usage of each object reference
is tracked - counted - and the object instance is automatically released
when it is no longer reachable through any strong reference. When two
object instances hold strong references to each other, they form a
reference cycle. The reference counting mechanism is not capable of
solving such cycles on its own, and requires developer intervention to
avoid and break such cycles.

While ARC is considered to be a form of garbage collection, the


behavioral differences are so great that it falls into a category of its own.

The most distinguishing feature of ARC is the fact that just like manual
memory management, ARC is deterministic in its nature. That means
memory deallocation happens at a predictable time and place during
execution. While the reference counting mechanism adds some
overhead compared to manual memory management, due to thread
locking while incrementing and decrementing the reference count, it is
still consistent and repeatable, and just as with manual memory
management, any bottlenecks can be easily identified and, if necessary,
optimized.

Each object instance also uses more memory under ARC, because of the
accompanying reference count variable.

1.3 Garbage Collection - GC


Garbage collection tracks all references, but in a different manner from
ARC, and can resolve reference cycles. It runs periodically behind the
scenes at unpredictable times - there is no deterministic way to say when
some unreachable object instance will be released, if ever.

Garbage collection uses a stop the world model, where the process of
collecting object instances stops all other tasks until it is complete, and
can take long enough to impose visible slowdowns during application
execution. It is a runtime memory management model. It runs and
analyses objects' graphs at runtime, deciding which instances are ready
to be released. On the other hand, ARC reference counting code is
inserted during compile time at particular places in the code and only the
incrementing and decrementing of the reference count happens at
runtime.

While GC does not suffer from invalid reference issues and memory
leaks - caused by forgetting to release the object instance, or by
reference cycles - GC applications can also experience memory
problems, but in a different way. Memory leaks under GC are created by
holding onto references to an object that are no longer needed by the
application. Holding references to such unnecessary instances can have
devastating effects on application performance and memory
consumption, and can eventually lead to Out of Memory exceptions. As
long as there is an active reference to some object instance, GC will not
reclaim it, and such references can easily hide inside some long-lived
root nodes. Since those leaks are usually not obvious, tracking them
down can be extremely difficult. Just as with any other memory
management system, GC also requires a certain amount of discipline
when writing code.

Generally, GC models require more memory at runtime than deterministic


systems where object instances are immediately deallocated when no
longer needed.

Anyone saying that you don't have to know or think about how memory
management works under any kind of automatic memory management
system is lying to you. The thing is that with automatic systems, any
issues you might have will bite you much later in the process. Much later,
which usually means fixing bugs and issues might cost you more.

On the other hand, automatic systems do simplify everyday programming


tasks a lot. There is a whole lot of code that you don't have to write and
with that you can focus more on meaningful code and not memory
managing ceremonies. Less code means it is easier to read and follow.

Automatic memory management systems are also much safer.


Depending on the system, they minimize or completely remove issues
related to all kinds of invalid references and other forms of memory
corruption. While following some design principles and clean code
guidelines - and avoiding bad coding practices - can minimize memory
safety issues under manual memory management, doing so still requires
much more attention and involvement from the developer.

It is no accident that today's widely used, general-purpose tools and


languages usually have some kind of automatic memory management in
place. Among general-purpose languages, only a handful of rare
exceptions don't have it. From that perspective, introducing ARC into
Delphi was a logical evolutionary step towards making the language and
toolset more attractive and more future proof.
2 How memory works
Before we dive into different data types and their anatomy and behavior,
let's take a look at how memory is organized and how it stores our data -
variables.

We can look at memory as a huge array of equally sized little boxes.


Each box has its address, a number representing the location of that
particular piece of memory. The size of the box is 8 bits (one byte) and is
called the smallest addressable memory unit. Depending on whether we
are dealing with a 32-bit or a 64-bit operating system, the system's
address size - usually referred to as pointer size - will be 32 or 64 bits,
respectively. In theory, on a 32-bit system we can address a total of 232 -
4 GiB (gibibyte) of memory and on a 64-bit system we could address a
whopping 264 - 16 EiB (exbibyte). In practice there are hardware
limitations imposed by CPU architecture and subsequently operating
systems.

Historically, there were and still are, systems with different byte sizes, as
well as systems where the byte (regardless of its size) was not the
smallest addressable memory unit. However, for the purposes of this
book, and in the context of Delphi memory management, we will assume
that a byte contains 8 bits and the smallest addressable size is one byte.
The only thing that can vary is pointer (address) size, and that can be
either 32 or 64 bits, depending on the compiler used.

The following images show how a continuous block of 8 bytes (selected


at random) is addressed in 32-bit and 64-bit systems, respectively.

While a computer's physical memory actually is one huge chunk of


addressable units, applications organize that memory into different
regions - notably the stack and the heap - for various purposes.
Regardless of where variables are actually stored, their basic
representation remains the same.

The variables we declare in our code are actually symbolic names


representing a continuous chunk of memory where its value will be
stored. Variable's type denotes what kind of data we can store in that
particular place as well as its size - variables can occupy one or more
bytes of memory. We are actually giving a name - identifier - to the
address of the first byte.

2.1 Playing with numbers


var
x: byte;

x := 42;

So, what have we done here?

We have decided, for some arbitrary reason, that we need to have a byte
labeled as x . Whether this is a good name for that particular value is
anyone's guess, but the computer doesn't care if it is called x , HALF-OF-A-2D-
VECTOR , This_marks_the_Spot or thisIsAnExample . On the other hand, other
programmers - including yourself a few years after you have written a
certain piece of code - may object to your seemingly random naming
scheme.

Through processes we don't need to understand, the computer has


selected address $AB580F23 to hold our byte-sized number. We can rest
comfortably in the knowledge that the Ultimate Answer to Life, the
Universe, and Everything will be safe there, and move on to the next
example.

var
x: integer;

x := 1024;

Now we want to declare that x is a 32-bit integer containing the number


"1024". This time, the computer will pick four consecutive bytes to store
the number, and let's say that the first of those bytes is... address
$AB580F23 again.

Oops. We may have retroactively deleted the Earth. (Since you are still
firmly on the ground, it's more likely that we simply edited the previous
image to represent an integer instead of a byte. This sort of thing doesn't
normally happen anyway.)

As noted above, x technically still points at only one byte - $AB580F23 -


but it also knows that it has to read or write four bytes, as we can see in
the image below.

Variables are, well, variable and we can change the values stored inside
them. To prevent the destruction of the Earth - if it is not already too late -
we can update our integer variable x and safely store "42" again... until
next time...
var
x: integer;

x := 1024;
...
x := 42;

The second assignment will change the value stored inside the four bytes
variable x occupies, but its address will not change - it is still located at
$AB580F23.
Simple, right?

2.1.1 Not so simple


var
x: TObject;

x := TObject.Create;

... That escalated quickly.

So far we have dealt with bytes and integers, which are fairly lightweight
and can be duplicated and thrown around with relative impunity. These
are often called value types, because their value is stored directly within
the space occupied by the variable. As a direct result of that fact,
assigning one value type variable to another will copy the stored value.
Once copied, changing the value of one variable will not change the
value of the other.
var
x, y: integer;

x := 42;
y := x;
x := 5;

The first assignment stores "42" inside variable x . The second


assignment copies that value to the variable y and at that point, both x
and y contain value "42". The last assignment changes the value stored
in x to "5", but that will not have any impact on y , which still contains "42".

Another important feature of value types is that their size is fixed and
cannot be changed at runtime. For the other kind of data, whose size can
vary and is often larger than the common data stored within value types,
we use reference types. When we are working with reference types, the
actual data is stored somewhere far away and the immediate value
stored in a variable of a reference type is a memory address called a
pointer or reference to the location where the actual data resides. Since a
pointer is an address to a memory location, its size is fixed, and
depending on the compiler it is either 32 or 64 bits.

Pointer or reference variables behave as value types. If we assign one


pointer variable to another, we are also creating a copy of a value, but
only a copy of the pointer. The data behind both copies will stay the
same. And if we change the data we can see that change using both
pointers. Copying only a reference without the data behind it is called a
shallow copy. As you might have guessed, value types use deep
copying, though the term itself is used in combination with reference
types, where making a deep copy does not copy the reference but the
actual data behind the original reference to a new place in memory, and
the new reference will point to the copied data.

Back to the x variant declared as TObject . TObject is a class, and classes are
reference types.

As before, x is at $AB580F23 because our artist loves recycling that


address. Because we are working in a 32-bit environment, x needs to be
four bytes long, so it is just as big as the integer from the previous
example.

The difference is, this time we are not storing the contents of our TObject
instance in the four bytes assigned to x . Instead, we are storing where we
can find that instance: address $5FAC6041, in a memory chip far far
away. (From a certain point of view.) Any number of TObject references
could be pointing at that same address, possibly even changing its
contents, but the only one we are aware of right now is x .
2.2 Initialization and instantiation
One thing we have been glossing over so far is that variables -
regardless of whether they're value types or reference types - need to be
initialized. While some variables (notably, global variables or the various
fields in an object instance) will default to 0 or some equivalent value,
most variable types just grab whatever is currently occupying that
particular chunk of memory and run with it. Some reference types are
always automatically initialized with nil pointers - these are called
managed types and will be explained more thoroughly later - but others
are not.

The important thing here is that whatever an uninitialized variable


contains, chances are it won't be what you are expecting. If it is a simple
value, anything you try to do with that value may output wildly varying
results, but probably will not crash the program outright. If it is a pointer,
though, you are likely to crash with one of several fatal errors, most of
them related to trying to access memory you shouldn't. The moral of the
story is that you should always initialize your variables before you use
them, unless you are absolutely certain that the compiler has taken care
of that part for you.

Now, it should be pretty obvious how our first two examples initialized
their variables. We just set a value, and that was it. Sure, we deleted
Earth along the way, but that's why we have Undo.

But how did we initialize our TObject example? What does TObject.Create do?
Is it magic, is it sufficiently advanced technology, or is it just not known to
us? Do we even need to know?

Of course we do! The basics, at least. The implementation details may


vary, but the basic principles are the same.
In the beginning, there was x , pointer to TObject , sitting comfortably at
$AB580F23. x always pointed at nil , and was not a very happy pointer.
Nobody wanted to play with a non-existent object, because they were
afraid of what might happen to them. This, as we will later learn, is not an
entirely realistic scenario with the classic Delphi compiler, because TObject
is only a managed type when using the ARC compiler.

Then came along the wise divine entity wizard programmer, who saw x 's
plight and took pity upon it. With a single assignment to TObject.Create , he
simultaneously created a new TObject and said, "Here is your TObject , x , far
away at $5FAC6041. Go now, and point the way towards it."

From that day, and until the programmer reassigned or deallocated it, x
would happily point to address $5FAC6041, in the distant land known
only as The Heap.

... Or we could skip the fairy tale and just say that TObject.Create
instantiates a new TObject instance on the heap and returns its address to
whatever called it so it can be stored - assigned to appropriate variable.

2.3 The stack, the heap, and the differences between


them
We have mentioned earlier that a computer's memory is not just a huge
pile of bytes to be randomly allocated without rhyme or reason. We also
mentioned the stack and the heap, but we did not go into what they are.

2.3.1 The stack

The stack contains what is known as automatically allocated memory.


Each thread in a program has its own stack, used by all of the functions,
procedures and methods running on that thread. Specifically, their
parameters, the address they output into, and any variables they have
declared are stored in stack frames. Each stack frame corresponds to a
single function call, and the size of a function's stack frame is known in
advance because the parameters and local variables consist solely of
pointers and value types, both of which have well-defined fixed sizes.
Between this and the fact that memory is always allocated or deallocated
at the top of the stack, stack-based memory allocation is a lot faster than
its heap-based counterpart.

If a function (or procedure or method) calls another function, this function


is pushed onto the top of the stack, and will stay at the top until it either
calls a function, or returns a result and gets popped from the stack. This
chain of pushing and popping continues until either there's nothing left to
pop and the task is complete, or the stack can no longer be expanded -
either because there's no memory left to allocate, or the stack has
reached its maximum size. In Delphi, this defaults to 1 MB.

The latter scenario is called a stack overflow, and usually means that
something has gone horribly wrong. Actually, you have called one
function too many. On the bright side, the user will immediately know
something is wrong, because the application will crash. Then again, the
user won't be too happy while he is reporting the issue, so maybe that is
not so good.

Recursive algorithms where a function calls itself repeatedly can cause


stack overflow exceptions when the algorithm reaches a certain depth -
when the data upon which it operates requires too many recursive
function calls.

If the function that throws the stack overflow is not recursive by design,
then it calls itself by mistake and is simply not designed to get out of that
loop.

2.3.2 The heap

We know that the stack only handles pointers and value types. We also
know that each stack is only accessible from a single thread. So, the
obvious question is, "What about the rest? Where do the pointers lead,
where is all the other data?"

The heap is the answer to that. While the stack always knows when,
where and how much memory it is going to allocate at any given time, the
heap has no such luxury. It doesn't know or care about where the data is
coming from or how big something is until the program tries to store it.
The heap merely keeps track of the free and occupied pieces of memory
in its realm.

This causes a lot of headaches for programmer and program alike, as


they must issue requests to allocate memory on the heap, keep track of
what they have stored there and clean up anything they are not using
anymore. On top of that, the heap can be accessed from multiple
threads, adding even more ways for things to go wrong.

Just like you can run out of stack memory, you can also run out of heap
memory and get the dreaded out of memory exception. Every time you
issue an allocation request to the heap, all the memory you requested at
that point must be allocated in one continuous chunk. If the heap cannot
find a free memory block of adequate size, it will deny your request and
an out of memory exception will be raised.

If your application has been a busy bee, allocating and deallocating a lot
of memory, especially in smaller different sized chunks, heap memory
can suffer from fragmentation. That means there is seemingly enough
free memory to approve a particular allocation request, but that free
memory is scattered around in small chunks that are not big enough to
satisfy that request.

How is heap memory organized internally? Organization is left to the


discretion of the memory manager being used, which can organize the
heap in any way.
3 Variables and types
3.1 Value, pointer and reference types, unmanaged
and managed types
So far we have touched on the definitions of value and reference types,
and we have mentioned managed and unmanaged types. Now we will
provide more focused definitions and categorization.

Based on their memory representation and specific behavior, data types


can be divided into:

Value based types


Pointers - typed and untyped
Reference based types
managed types
unmanaged types

Not all high-level OOP languages have raw pointer types. However,
Delphi does, and even though reference types are a special kind of
pointers, it is important to make a clear distinction between the two. The
similarities are only superficial, and they are fundamentally different from
a memory management point of view.

Since managing raw pointers either falls into a legacy pre-objects


category or a more advanced interoperability topic, it will only be briefly
mentioned. The primary focus of this book is managing object instances,
and those are reference types.

From a memory management point of view, it is important to distinguish


between managed and unmanaged types. Managed types are those for
which memory is automatically managed by the compiler, and
unmanaged types are those for which we must write the appropriate
allocation and deallocation code.

While all value types are actually managed by the compiler, the
terminology managed is specifically used for automatically managed
reference types.

There is one note about pointer types - a pointer on its own is actually a
value type. What makes them special is that for some pointer types, we
can allocate and deallocate memory manually. Those are the pointer
types we refer to when using the terms unmanaged or allocatable pointer.

For instance, we can have a pointer to an integer, that points to the


address of some integer-declared variable. We don't have to allocate and
deallocate memory for that particular pointer because that will be handled
by the compiler through the integer variable. On the other hand, we can
use the same integer pointer variable, and allocate and deallocate
memory for the integer ourselves. If we manually allocate memory for any
pointer, we also have to release that memory in order to avoid memory
leaks - this is what makes some pointers unmanaged.
var
x: Integer;
p: ^Integer;

x := 7;
p := @x;

var
p: ^Integer;

New(p);
p^ := 3;
Dispose(p);

Some pointers, like procedural and method pointers or class references,


point to specific places in memory where some already allocated data or
code resides. We are not in charge of allocating and deallocating that
memory, but those pointer types still do not fall into the managed
reference types category, because those have special treatment from the
compiler for initialization purposes.

Typed pointers can point only to a specific type and untyped ones are
general-purpose pointers that can point to any memory location.

Value based types - all auto managed


simple types - boolean, integers, floats, characters, enumerations,
subranges
sets
records
file types
short strings
static arrays
Pointers - typed and untyped
procedural types
class references
allocatable pointers - management depends on particular case
Reference based types
Managed reference types
strings
dynamic arrays
variants
interfaces
anonymous methods - method references
objects in ARC compiler
Unmanaged reference types
objects in the classic compiler

3.1.1 Copy on Write - COW

Managed types can be managed in many different ways. Delphi strings


behave like value types, though they are actually implemented as
reference types. They achieve that by using copy-on-write semantics.
Since they are implemented as references, the actual string data sits on
the heap. If we assign one string variable to another, they will end up
pointing to the same data. This is actually a good thing, as it saves
memory and speeds up program execution. Copying only the reference is
way faster than copying the accompanying data, especially when the
data occupies large amounts of memory.

As long as we are just reading such a shared string, there will be only
one copy. When we decide to change the value of the string, copy-on-
write will kick in. The contents of the string will be copied before it is
modified, and the code that tries to change it will actually get its own copy
to write to.
Delphi uses copy-on-write only for strings, however copy-on-write is a
widely used memory management concept that can be applied to other
types and situations.

3.1.2 Inspecting the pointer or reference content

There are three ways to inspect the contents of any pointer or reference:

Type casting the pointer or reference variable to NativeUInt . The size


of NativeUInt is equal to the size of the pointer, and it will be 32 or 64
bits, depending on the compiler used. The resulting number is the
memory address where the pointer points to, where the value 0
represents nil .

The Assigned function - Assigned(p)

Checking for nil expressions - p = nil or p <> nil

3.2 Automatic initialization of variables


All managed reference types are always automatically initialized. For the
others, the place of declaration determines whether or not they are
automatically initialized.

Automatically initialized variables are:

Managed reference types


Class fields
Object instance fields
Allocations with AllocMem
Global variables - with the exception of the main program's begin...end
block

Basically, local variables are uninitialized unless they are managed


reference types. Variables belonging to the main program's begin...end
block have special treatment. Usually, they are automatically initialized,
but some of them - for example, loop indexes - can be treated like local
variables.
3.3 Invalid pointers or references - wild, dangling,
stale
There are a few types of invalid pointers - non-nil pointers which don't
point to valid, allocated memory locations:

wild, uninitialized - pointers or references that were never initialized


dangling - pointers or references that point to previously allocated memory
which has been deallocated since then
stale - pointers or references are similar to dangling pointers, pointing to
previously allocated memory that has been deallocated, but through another
pointer/reference

Accessing any kind of invalid pointer or reference results in undefined


behavior - as in anything can happen: the application can crash, it can
seemingly work as expected, or it can work containing wrong or
corrupted data and what is worse, what will happen cannot be predicted.
For instance, the application may work normally most of the time, but
once in a while it may crash.

Since unpredictable behavior is common for all invalid pointers or


references, the terms above are often used interchangeably regardless of
their accuracy.

One of the issues with invalid pointers is that during application execution
we cannot distinguish an invalid pointer from a valid one. There are some
debugging techniques and memory managers that can help us identify
such pointers, but they should be taken more as fire extinguishers. What
we really want is to prevent writing code that can cause fire in the first
place.

Checking any kind of invalid pointer for nil - either directly or using the
Assigned function - will give us a wrong result because both checks only
inspect the contents of the pointer or reference variable itself and not the
validity of what it references.

The following examples (for classic compilers) use the fact that
unmanaged local variables - including object references - are not
initialized by default. Since accessing invalid pointers is undefined, the
behavior and output of those examples may differ when compiled by
different compilers and when run on different computers. I tried to
compose all examples to exhibit erroneous (undesirable) output. If you
run those examples and get different results, take that as additional proof
for undefined behavior.

3.3.1 Wild reference


program Wild;

procedure Proc;
var
Obj: TObject;
begin
Writeln('Object address ', NativeUInt(Obj), ', is nil ',
Obj = nil, ', assigned ', Assigned(Obj));
Writeln(Obj.ToString);
end;

begin
Proc;
end.

Object address 4217544, is nil FALSE, assigned TRUE

First chance exception at $00405A56. Exception class $C0000005 with message


'access violation at 0x00405a56: read of address 0x408dc337'. Process Wild.exe

In the above example, Obj represents a wild reference. It was not


initialized before we used it, and it points to some random memory
location. Calling the ToString method on such an uninitialized reference
results in an Access Violation exception and an application crash. As
previously said, testing a reference for nil is an exercise in futility as it will
fail, giving us the illusion that Obj points to a valid memory location. Even
with the following modification we will get an AV exception.
procedure Proc;
var
Obj: TObject;
begin
Writeln('Object address ', NativeUInt(Obj), ', is nil ',
Obj = nil, ', assigned ', Assigned(Obj));
if Assigned(Obj) then Writeln(Obj.ToString);
end;
Since any uninitialized variable contains whatever value was previously
written in that particular memory location, it is possible that some code
combination may clear the memory location of our variable as though it
had been properly initialized to nil . But you cannot rely on such
behaviors.

The following is an example of such seemingly working code. Local


variables reside in stack frames. When the first procedure Clear exits, the
next procedure call, Proc , will reuse that stack frame along with whatever
values remained.
program WildWorking;

procedure Clear;
var
x: NativeUInt;
begin
x := 0;
end;

procedure Proc;
var
Obj: TObject;
begin
Writeln('Object address ', NativeUInt(Obj), ', is nil ',
Obj = nil, ', assigned ', Assigned(Obj));
if Assigned(Obj) then Writeln(Obj.ToString)
else Writeln('Obj is nil');
end;

begin
Clear;
Proc;
end.

Object address 0, is nil TRUE, assigned FALSE


Obj is nil

Now imagine the above scenario happening in more complex code,


where previously executed code can leave just about any values in
memory and just about anything can happen.

3.3.2 Dangling reference


program Dangling;
uses
System.Classes;

procedure Proc;
var
Obj: TObject;
Owner, Child: TComponent;
begin
// initialize Obj to nil for testing purposes
Obj := nil;
Writeln('Obj initialized');
Writeln('Obj address ', NativeUInt(Obj), ', is nil ',
Obj = nil, ', assigned ', Assigned(Obj));

Obj := TObject.Create;
Writeln('Obj created');
Writeln('Obj address ', NativeUInt(Obj), ', is nil ',
Obj = nil, ', assigned ', Assigned(Obj));
Writeln(Obj.ToString);
Obj.Free;

// at this point, Obj is a dangling pointer


Writeln('Obj released');
Writeln('Obj address ', NativeUInt(Obj), ', is nil ',
Obj = nil, ', assigned ', Assigned(Obj));
Writeln(Obj.ToString);

// Some unrelated code that will overwrite memory


Owner := TComponent.Create(nil);
Child := TComponent.Create(Owner);
Writeln('Obj address ', NativeUInt(Obj), ', is nil ',
Obj = nil, ', assigned ', Assigned(Obj));
Writeln(Obj.ToString);
Owner.Free;
end;

begin
Proc;
end.

Obj initialized
Obj address 0, is nil TRUE, assigned FALSE
Obj created
Obj address 15797696, is nil FALSE, assigned TRUE
TObject
Obj released
Obj address 15797696, is nil FALSE, assigned TRUE
TObject
Obj address 15797696, is nil FALSE, assigned TRUE
TMoveArrayManager<System.Classes.TComponent>

The above example illustrates the behavior of a dangling reference.


Initialization to nil could be omitted in regular code, its only purpose here
is to show that we are starting with a clean reference that will
subsequently point to our newly created object instance.

The first two value outputs are as expected - after initialization, Obj is nil ,
and after creation Obj is no longer nil and points to the memory address
(15797696) where the object instance is stored. Calling the ToString
method will show the class name TObject . It is important to note that with
each program execution, the actual memory address value can change,
but unless we assign another object instance or nil to the Obj variable, it
will keep the same value during a single execution.

The problems start when we release the object instance Obj refers to.
Afterthe object instance is released, its memory is no longer valid and Obj
has been freed, yet all value outputs are just the same as they were
when Obj pointed to a valid object instance. That is exactly issue with
dangling object references. At runtime we cannot tell the difference
between valid and dangling object references. We have to look at the
code and have to spot the logical error - using an object instance that we
have already released.

And it gets worse. After we created some more object instances we


encountered a situation where the memory previously occupied by the Obj
instance has been overwritten with some other data. Calling the
Obj.ToString method will now show completely unrelated results, the wrong
class name TMoveArrayManager<System.Classes.TComponent> .

3.3.3 Stale reference


program Stale;

uses
System.Classes;

procedure Proc;
var
Obj, Ref: TObject;
Owner, Child: TComponent;
begin
Obj := TObject.Create;
Ref := Obj;
Writeln('Obj created, Ref assigned');
Writeln('Obj address ', NativeUInt(Obj), ', is nil ',
Obj = nil, ', assigned ', Assigned(Obj));
Writeln('Ref address ', NativeUInt(Ref), ', is nil ',
Ref = nil, ', assigned ', Assigned(Ref));
Writeln(Ref.ToString);
Obj.Free;
Obj := nil;

// at this point, Ref is a stale pointer


Writeln('Obj released');
Writeln('Obj address ', NativeUInt(Obj), ', is nil ',
Obj = nil, ', assigned ', Assigned(Obj));
Writeln('Ref address ', NativeUInt(Ref), ', is nil ',
Ref = nil, ', assigned ', Assigned(Ref));
Writeln(Ref.ToString);

// Some unrelated code that will overwrite memory


Owner := TComponent.Create(nil);
Child := TComponent.Create(Owner);
Writeln('Ref address ', NativeUInt(Ref), ', is nil ',
Ref = nil, ', assigned ', Assigned(Ref));
Writeln(Ref.ToString);
Owner.Free;
end;

begin
Proc;
end.

Obj created, Ref assigned


Obj address 40760912, is nil FALSE, assigned TRUE
Ref address 40760912, is nil FALSE, assigned TRUE
TObject
Obj released
Obj address 0, is nil TRUE, assigned FALSE
Ref address 40760912, is nil FALSE, assigned TRUE
TObject
Ref address 40760912, is nil FALSE, assigned TRUE
TMoveArrayManager<System.Classes.TComponent>

The above example shows a similar scenario and outputs as the dangling
reference example, with a few differences. First, we now have two object
references, Obj and Ref , pointing to the same object instance - a perfectly
valid scenario. And second, after we released the object instance, we
also set its original reference Obj to nil. However, that didn't get us far. We
have reinitialized one reference, but we didn't reinitialize the other.
Regardless of what we do with the Obj variable, the variable Ref will still
point to an invalid memory location representing a stale reference. Stale
references may be a bit harder to spot in code than dangling ones,
because we have more than one identifier in play. In other words, it is
easier to see the error in the Obj.Free; ... Obj.DoSomething; sequence than in
the Obj.Free; ... Ref.DoSomething; sequence.

Note: The Delphi NextGen ARC compiler minimizes the possibility of


encountering invalid references. All the above examples used as-is would
work correctly if compiled with the ARC compiler. The only references
that can end up being invalid are ones marked as [unsafe] or stored in raw
pointers.

3.4 Scope and lifetime


The scope of a variable is determined by its declaration. A scope is the
area of code where a variable can be used - where its identifier is known
and it can be accessed. In other words - the place of declaration
determines the scope.

During program execution, entering scope is the point where the variable
becomes accessible by any part of the code. It is said that a variable
leaves its scope when code execution reaches the point from where the
variable is no longer accessible by any part of the code.

The lifetime of the variable is tightly coupled with its scope. Its lifetime will
begin just before it enters scope and will end just after it exits scope. Any
automatic initialization or finalization process will happen at that point
where the variable lifetime begins or ends.

However, the lifetime of the data behind the variables, while dependent
on variable scope, is not absolutely defined by it. When a variable
declared as a value type leaves its scope, the data associated with it is
gone. When a reference type variable leaves its scope, only the
reference itself is gone. The data behind the reference may have a
different lifetime from that of the reference itself, depending on whether or
not there are other references to that data, or whether we are dealing
with a managed or unmanaged reference type.

If a variable is an unmanaged reference type and there are no other


variables pointing to the data behind the reference, the developer is
responsible for releasing the allocated memory before that particular
reference goes out of scope. Failure to do so will result in memory leaks.

To make life less boring, the term scope carries some ambiguity. It can be
connected with the accessibility or the lifetime of the variable.

Wait a minute. Didn't we just said that lifetime depends on accessibility?


Err... Yes, but what has not been said is that accessibility is also
determined by visibility. In other words, visibility can limit accessibility, but
will not have any influence on lifetime.

As far as memory management is concerned, we are more often


interested in scope in terms of lifetime than visibility.

Having said that, the often used phrase in the context of clean and well
designed code about limiting scope covers both aspects. It actually
advises limiting the lifetime of the variable as much as possible and then
limiting the visibility as much as possible.

And now, let's shed some light on the whole scope, accessibility, visibility
and the lifetime mess.

If we declare a variable in the interface section of the unit, that variable


will be globally accessible from any other unit or program that has this
unit in its uses section. The lifetime of that variable will begin with
initialization of the unit where it is declared and end with its finalization.
However, if we move that variable from the interface section of the unit to
the implementation section we will limit its visibility and its scope in terms
of accessibility, since it will no longer be accessible by any outside unit or
program. But its lifetime will still be the same - beginning with initialization
and ending with finalization.

The same principle applies to declaring variables inside a class - fields.


The lifetime of object instance fields always begins and ends with the
lifetime of object instance, but using visibility specifiers - private , protected ,
public , published - we can limit that field's visibility and accessibility.

3.4.1 Scoping terminology

The following are some commonly used phrases. Their definition should
not be taken too literally because they are often used in different contexts
where the exact meaning can slightly vary.

Local scope

Variables declared inside function or procedures or methods have a local


scope. They are only accessible within the body block of the function or
procedure or method, and all of the blocks enclosed within. Since local
variables don't have any visibility specifiers, their lifetime matches their
accessibility or vice versa. Each time the routine is executed, its variables
will start a new lifecycle.

In Delphi, the minimum scope in terms of lifetime is routine scope


(function, procedure, method). That is important to keep in mind for
implicit local references, as they will go out of scope at the end of the
routine and not sooner. There are some exceptions to that rule, where
some implicit references can have a smaller scope that the routine itself.
Particular cases that have impact on memory management will be
covered later in the book in appropriate places.

It is safe to say that you should expect any implicit local variable's scope
to match the routine scope.

Global scope

Variables declared in the interface section of the units have global scope.
They are accessible from the very beginning of the program or module
that uses a particular unit to its end, and their lifetime starts with the
initialization of the unit and ends with its finalization.

Unit scope

The scope of the variables declared in the implementation section of a


unit is limited to that unit's implementation section only. That means they
are not accessible from the outside. However, the lifetime of such a
variable is the same as that of any variable declared in the interface
section of that unit.

Instance scope

Variables declared within a class are called fields. They are directly
accessible from within the class, depending on the declared visibility
specifiers, and indirectly through instance references if they have public
or published visibility. Their lifetime matches the instance's lifetime.

Class scope

Class fields' lifetime begins before the initialization section of the unit
where a class is declared and ends after the finalization section of that
unit. Their accessibility depends on their visibility specifiers. Class fields
are basically global variables in disguise.

Record scope

Variables declared within records follow the same rules as variables


declared within classes. Their lifetime begins and ends with the record's
lifetime. The main difference is in the reduced number of available
visibility specifiers, as records don't support inheritance.

3.4.2 Name conflicts

Delphi does not allow two identifiers with the same name declared in the
same block. Since every variable must be accessible within its defined
scope, there is a possibility of name conflicts when the scope of different
variables overlaps.

While name conflicts don't have any direct influence on memory


management, indirectly they can impact it because we may think that
some identifier represents one particular variable and the compiler may
not agree. Since we must live by what the compiler thinks is right, we
must follow the rules it enforces.

When a name conflict occurs, the compiler always prefers declarations in


the inner block - in other words, the declaration that comes from the
closest, more localized scope. If we have to override that and access the
variable declared in the outer block we can do that by using its fully
qualified name - including the identifier of the outer scope block.
program Scope1;

var
x: Integer;

procedure Proc;
var
x: Integer;
begin
x := 5;
Writeln('Local ', x);
// we are accessing x from outer scope with fully qualified name
Writeln('Global accessed from Proc ', Scope1.x);
end;

begin
x := 3;
writeln('Global ', x);
Proc;
Writeln('Global ', x);
Readln;
end.

Running the above example will give the following output:


Global 3
Local 5
Global accessed from Proc 3
Global 3

There are two variables named x . One has global scope, the other has
local scope - for procedure Proc . When there are two variable names
accessible in the same block of code, the compiler will prefer the one with
the more localized scope. That is why assigning a value to x inside the
procedure will change the value of the local variable and not the global
one.

If we remove the global declaration of variable x , we will get the following


error for the assignment x := 3;

[dcc32 Error] Scope2.dpr(12): E2003 Undeclared identifier: 'x'


program Scope2;

procedure Proc;
var
x: Integer;
begin
x := 5;
Writeln('Local ', x);
end;

begin
x := 3;
writeln('Global ', x);
Proc;
Writeln('Global ', x);
Readln;
end.

However, if we remove the local variable declaration, we will be able to


compile and run the program.
program Scope3;

var
x: Integer;

procedure Proc;
begin
x := 5;
Writeln('Local ', x);
end;

begin
x := 3;
writeln('Global ', x);
Proc;
Writeln('Global ', x);
Readln;
end.

And we will get the following output:


Global 3
Local 5
Global 5
The variable x has global scope and is accessible from within the
procedure Proc . Since we no longer have a local variable with a conflicting
name, assigning a value to x inside Proc will change the value of our
global variable x .
4 Classes
Classes are an unambiguously defined combination of values which may
be capable of performing certain functions using those (or other) values.
In other words, they define an extendable structure consisting of fields,
methods and properties. They define a template from which object
instances are constructed.

Historically, records (or structs, as they are called in C) could be


considered as predecessors to classes in some ways. They define a set
of variables that is meant to be used together, but are not typically
capable of doing anything on their own.

Delphi extended the classical definition of records in Delphi 2006 and


added support for methods and properties. But the defining differences
are that records cannot be extended or inherited from, and that the
memory used by records is always managed automatically by the
compiler as they are value types, while object instances need some more
programmer attention.

A bit more insight into records and classes from Joe C. Hecht:

Classes and objects are really no more than ordinary records containing
function tables, and a pointer to self, used to neatly encapsulate data
(now called properties) with the functions (now called methods) that
manipulate the data. A pointer to self is passed to each function to allow
the methods to find the record with which they are associated.

In fact, if you want to roll up your sleeves, you can extend any language
that does not directly support object oriented programming, providing the
language supports function variables (most often often called function
pointers) to allow the language to support OOP by simply adding a
function table and a pointer to self to a record! In fact, if you explore the
history of C++, you will find that its first existence was constructed on C
with macros.
Inheritance is formed when new records descend from other records, and
concepts such as virtual functions are put into place by copying,
manipulating, and extending the function tables in the record.

Luckily, most modern compilers support the concept of classes (and or


objects), and manage all this work for us, (including secretly passing the
pointer to self to the methods).

Believe it or not, there are cases where you might still need to roll up your
sleeves and do this sort of thing, for example, extending a language such
as "C" that does not directly support Object Oriented Programming to
support an object oriented framework that you design yourself.

4.1 Fields
Variables declared within classes (and records, for that matter) are called
fields. They are just ordinary variables that can be accessed only through
class instances - objects.

Classes can also define fields that belong to the class itself using the
class prefix.

4.2 Properties
Properties define an interface for accessing specific data in the class.
They can be directly mapped to fields, or can be accessed using
methods called property getters and setters - as a result, they don't have
to actually exist at any particular place in memory.

Since they only represent an access interface, they don't have any
associated memory management requirements or rules.

4.3 Methods
Methods are not all that dissimilar to ordinary functions and procedures.
They take parameters, they do things with them, and they may or may
not return a value after they are done.
On the other hand, there are some notable differences, and a lot of ways
to further modify their behavior.

4.3.1 Method directives

Method directives are part of a method declaration and must be declared


in a particular order. The required ordering is merely a compiler whim,
and the order, even if the compiler allowed a different one, has absolutely
no influence on their meanings.

reintroduce;
overload;
virtual | dynamic | override;
register | pascal | cdecl | stdcall | safecall;
abstract;
platform; deprecated; library;

Directives separated with | are mutually exclusive.

Besides following the ordering and exclusivity rules, there are no other
strict requirements for method directives, and methods can have zero or
more combinations of the listed directives.

4.3.2 Self

Since methods are declared as part of a class, and a single class can
have many instantiated objects, we need a binding mechanism between
methods and a particular class instance. This is done with Self , a special
identifier that is accessible from within methods and refers to the
particular object instance upon which the method has been called upon.

Self is actually a hidden parameter passed to a class' methods,


containing a reference to the instance the method is being called from. It
is functionally equivalent to the this keyword in languages such as C++ or
Java, and can be used for situations like these:
type
TPart = class;

TShip = class(TObject)
private
// Some kind of array or collection of TPart instances.
// The details don't matter right now.
public
destructor Destroy; override;
procedure AddPart(APart: TPart);
end;

TPart = class(TObject)
public
constructor Create(AShip: TShip);
end;

implementation

destructor TShip.Destroy;
begin
// Make sure to do whatever destruction is necessary on our part storage clas
inherited;
end;

procedure TShip.AddPart(APart: TPart);


begin
// Add APart to whatever we're using to store parts.
end;

constructor TPart.Create(AShip: TShip);


begin
inherited Create;
// Our constructor's parameter list differs from that of our parent,
// so we need to be more specific while invoking its constructor.

// Tell the ship to add us to its part list.


AShip.AddPart(Self);
end;

Technically, we could have approached that differently - for instance,


declaring a variable of type TPart , constructing it with a parameterless
constructor, and telling our TShip to add that TPart to its parts - but that
would not have demonstrated how Self works.

Delphi does not require that all access to an object's fields are prefixed
with Self . It is commonly only used when we need to reference the object
itself.

4.3.3 inherited
is used to invoke the methods of a parent class. If the
inherited
parameters in the parent class match the parameters of the method from
which inherited is called, then you can simply call inherited without
specifying anything else. Otherwise, the method must be called as usual,
but prefixed with the inherited keyword.

Here's one of the more common examples of the former kind of inherited
call - constructors and destructors:
type
TQuestion = class(TObject)
public
FAnswer: string;
constructor Create;
destructor Destroy; override;
end;

implementation

constructor TQuestion.Create;
begin
// Calls the parent class' constructor
inherited;

// Do whatever special initialization our class requires.


// For instance, let's answer all questions with "42" until
// they receive a different answer.
FAnswer := '42';
end;

destructor TQuestion.Destroy;
begin
// Do whatever special destruction our class requires.
// In this case, we don't actually need to define a destructor,
// because there is nothing to destroy.

// Calls the parent's destructor. This should always be done in a destructor.


inherited;
end;

And here's an example of the other kind of inherited call, located in the
TChild.DifferentPrint method:

type
TParent = class
public
// The virtual keyword is necessary for method overriding.
procedure Print; virtual;
end;

TChild = class(TParent)
public
procedure Print; override;
procedure DifferentPrint;
end;

procedure TParent.Print;
begin
WriteLn('This is the parent''s output.');
end;

procedure TChild.Print;
begin
// We are not calling inherited here because we want to print our own stuff.
WriteLn('This is the child''s output.');
end;

procedure TChild.DifferentPrint;
begin
// Calls TChild's immediate parent's Print function.
// In this case, that's TParent.Print.
inherited Print;
end;

var
c: TChild;
begin
c := TChild.Create;
c.Print;
end.

Executing the above code will result in the following output:


This is the child's output.

Calling DifferentPrint will output:


This is the parent's output.

4.4 Method binding


We commented on our usage of the virtual keyword in one of the above
examples, saying it was necessary for method overriding, but it was not a
good time to explain how and why it was needed. This is related to the
subject of method binding - determining the location of a method so that it
can be called at runtime.

Delphi supports both static binding, where a function's address is


determined at compile time as an offset from the beginning of the
application, and dynamic binding, where the function's address is
determined at runtime.

4.4.1 Static binding


By default, methods in Delphi are statically bound. Calling a static
method will select the method implementation used by the declared type
of the variable in question, regardless of whether the object's actual class
has a different implementation:
type
TRectangle = class(TObject)
public
procedure Draw;
end;

TSquare = class(TRectangle)
public
procedure Draw;
end;

procedure TRectangle.Draw;
begin
Writeln('Draw rectangle');
end;

procedure TSquare.Draw;
begin
Writeln('Draw square');
end;

var
Rectangle: TRectangle;
Square: TSquare;

begin
Rectangle := TRectangle.Create;
// Calls TRectangle.Draw
Rectangle.Draw;
Rectangle.Free;

Rectangle := TSquare.Create;
// Still calls TRectangle.Draw,
// even though the TSquare subclass has a Draw function of its own
Rectangle.Draw;
// Casts Rectangle to a TSquare and calls TSquare.Draw on it.
// This would crash the program if Rectangle weren't a TSquare.
TSquare(Rectangle).Draw;
Rectangle.Free;

Square := TSquare.Create;
// Calls TSquare.Draw
Square.Draw;
Square.Free;
end.

Running the above code yields the following output:


Draw rectangle
Draw rectangle
Draw square
Draw square

As it happens, just about any sane TRectangle implementation can correctly


draw a TSquare because squares are a subset of rectangles - but a TSquare
might be capable of doing it faster, depending on its implementation.

The trouble is, we don't necessarily know when we're dealing with a
TSquare instead of a TRectangle . This is where dynamic binding comes in.

4.4.2 Dynamic binding

With dynamic binding, method calls use the method implementation of


whatever class a specific object belongs to. If a certain subclass doesn't
have its own implementation of the method being called, then it will just
use the parent's implementation.

Delphi provides two keywords for dynamic binding: virtual and dynamic .
Both of them achieve the same effect - the difference is in how they do it.
In short, virtual methods are optimized to work as quickly as possible,
whereas dynamic methods generate smaller code, but work more slowly.

In principle, virtual methods are preferable for dynamic binding. However,


if a class has a lot of subclasses, and only a few of those subclasses
need to override a certain method, then it may be useful to declare it as
dynamic . Really, you should stick to virtual unless there is a clearly
observable improvement to be had.

4.4.3 How to use dynamic binding

It is all very well to know the keywords and the theory of how dynamic
binding works, but none of that is any good if you don't know how to
apply it. How does one use dynamic binding in Delphi?

A dynamically bound method is defined like this:


type
TSomeObject = class(TObject)
public
// We could just as easily have used 'dynamic' here.
procedure DoSomething; virtual;
end;

implementation

procedure TSomeObject.DoSomething;
begin
WriteLn('I identify with the number 42.');
end;

The thing is, this does not really do much on its own. We don't have any
subclasses overriding DoSomething - we don't have any subclasses at all,
really - and there are numerous things we could do beyond simply
overriding it.

So, let's start with something simple, shall we?

4.4.4 Method overriding

The override directive tells the compiler that we want to replace calls to our
parent's method with calls to our method, regardless of what we are
being labeled as.

For instance, let's make a subclass of TSomeObject called TAnotherObject . We


want TAnotherObject to always print out "I prefer the number 47." whenever
an instance of TAnotherObject is told to execute DoSomething , even if its
containing variable is defined as a TSomeObject .
type
TAnotherObject = class(TSomeObject)
public
procedure DoSomething;
end;

implementation

procedure TAnotherObject.DoSomething;
begin
WriteLn('I prefer the number 47.');
end;

Now, if we run this code:


var
AnOverrideTest: TSomeObject;
begin
AnOverrideTest := TSomeObject.Create;
AnOverrideTest.DoSomething;
AnOverrideTest.Free;

AnOverrideTest := TAnotherObject.Create;
AnOverrideTest.DoSomething;
AnOverrideTest.Free;
end.

we might expect to get this output:


I identify with the number 42.
I prefer the number 47.

Right?

Wrong. We will actually get this:


I identify with the number 42.
I identify with the number 42.

We will also get a compiler warning along the lines of this:


W1010 Method 'DoSomething' hides virtual method of base type
'TSomeObject'

Oops. It turns out we forgot to override TSomeObject.DoSomething , and instead


simply hid it from any code we might have written in TAnotherObject . We will
look into some of the deeper implications of this later - for now, our test
makes it look as if we had simply statically bound both functions.

Let's fix our type declaration and try again:


type
TAnotherObject = class(TSomeObject)
public
procedure DoSomething; override;
end;

Now, our output will really be what we expected:


I identify with the number 42.
I prefer the number 47.

4.4.5 Final methods

Let's try redeclaring TAnotherObject.DoSomething and create a subclass of


TAnotherObject , like this:

type
TAnotherObject = class(TSomeObject)
public
procedure DoSomething; override; final;
end;

TYetAnotherObject = class(TAnotherObject)
public
procedure DoSomething; override;
end;

Then let's try compiling again. The program will fail to compile,
generating this error:
E2352 Cannot override a final method

What have we done here?

By adding the final keyword to our method declaration, we have (rather


whimsically, in this case) told the compiler that TAnotherObject.DoSomething
must not be overridden by its descendants. This has the side-effect of
letting the compiler perform some slight optimization, but its primary
purpose is to be a giant, flashing red neon sign saying "DO NOT
OVERRIDE" for anyone who sees the code. And if they don't take the
hint... well, you can wish them luck compiling through an error.

There are three things we can do to solve the issue we've created:

Remove the final directive from TAnotherObject.DoSomething's


declaration
Make TYetAnotherObject derive from TSomeObject instead of
TAnotherObject
Or remove DoSomething from TYetAnotherObject.

Depending on the situation, any of those solutions can be valid - in this


case, the third one removes the point of creating the TYetAnotherObject class,
but the other two are perfectly viable under the circumstances.

4.4.6 Abstract methods

Now let's try redeclaring TSomeObject.DoSomething like this, and see what the
compiler does:
procedure DoSomething; virtual; abstract;

The compiler will throw an error, and a warning:

[Error] No definition for abstract method 'DoSomething' allowed

[Warning] Constructing instance of 'TSomeObject' containing abstract


method 'TSomeObject.DoSomething'
This is because we have declared DoSomething as an abstract method, then
tried to give it an implementation.

Abstract methods aren't implemented in the class that defines them -


instead, each subclass must implement them on its own. Attempting to
call an unimplemented abstract method will cause the program to throw
an EAbstractError exception at runtime - attempting to explicitly instantiate
an abstract class, like we did with TSomeObject above, will result in either a
warning or an error during compilation, depending on your project
settings.

4.4.7 The reintroduce directive

The reintroduce directive, inserted before any other directives, stops the
compiler from throwing warnings about hidden methods. It also tells other
programmers, including your future self, that you meant to hide a certain
method, and that it should stay this way.

4.4.8 The differences between overriding, hiding, or doing nothing


with a virtual method

We now know how to override a method. We've also learned how to hide
a method. We already knew how to do nothing.

But how do these different approaches work?

Let's demonstrate on the following example and its output:


type
TType1 = class(TObject)
public
procedure OverriddenMethod; virtual;
procedure HiddenMethod; virtual;
procedure IgnoredMethod; virtual;
end;

TType2 = class(TType1)
public
procedure OverriddenMethod; override;
procedure HiddenMethod; reintroduce; virtual;
procedure TestMethod;
end;

procedure TType1.OverriddenMethod;
begin
WriteLn('TType1 Override');
end;

procedure TType1.HiddenMethod;
begin
WriteLn('TType1 Hidden');
end;

procedure TType1.IgnoredMethod;
begin
WriteLn('Calling Override ...');
OverriddenMethod;
end;

procedure TType2.OverriddenMethod;
begin
WriteLn('TType2 Override');
end;

procedure TType2.HiddenMethod;
begin
WriteLn('TType2 Hidden');
end;

procedure TType2.TestMethod;
begin
WriteLn('Internal inheritance test ...');
inherited OverriddenMethod;
OverriddenMethod;
inherited HiddenMethod;
HiddenMethod;
inherited IgnoredMethod;
IgnoredMethod;
end;

var
Object1: TType1;
Object2: TType2;

begin
Object1 := TType1.Create;
Object1.OverriddenMethod;
Object1.HiddenMethod;
Object1.IgnoredMethod;
Object1.Free;
Object1 := TType2.Create;
Object1.OverriddenMethod;
Object1.HiddenMethod;
Object1.IgnoredMethod;
Object1.Free;

Object2 := TType2.Create;
Object2.OverriddenMethod;
Object2.HiddenMethod;
Object2.IgnoredMethod;

Object2.TestMethod;
Object2.Free;
end.

TType1 Override
TType1 Hidden
Calling Override...
TType1 Override

TType2 Override
TType1 Hidden
Calling Override...
TType2 Override

TType2 Override
TType2 Hidden
Calling Override...
TType2 Override

Internal inheritance test...


TType1 Override
TType2 Override
TType1 Hidden
TType2 Hidden
Calling Override...
TType2 Override
Calling Override...
TType2 Override

This is a big one, so let's break it down a little.

As we have established, OverriddenMethod will always use the


implementation provided by the type of the specific TType1 instance in
question. We see this quite nicely in the first two blocks of the example
output.
We have also established that HiddenMethod will act like a static method,
basing its behavior on its variable type rather than its instance type. This
is also nothing new, but we have learned to suppress warning W1010 by
using the reintroduce keyword in TType2.HiddenMethod .

We can also notice that while we only provided an implementation for


TType1.IgnoredMethod , TType2 has no qualms about using that implementation.
This would also have happened if IgnoredMethod were statically bound, but in
this case we want to emphasize the fact that a class does not have to
override all of its parent's virtual methods. Notice, however, that our call
to OverriddenMethod still produces a different result when IgnoredMethod is being
called on a TType2 instead of a TType1 .

Finally, we take a look at how all of these various methods are seen from
the perspective of TType2 . When we make inherited calls to the methods, we
always get TType1 's implementation, but when we make a regular call, we
get the same implementation we would get if we had called the methods
directly on Object2 without the TestMethod wrapper: TType2.OverriddenMethod ,
TType2.HiddenMethod , and TType1.IgnoredMethod .

4.4.9 Method overloading

A few pages ago, we demonstrated static binding on the TRectangle and


TSquare classes, which handled their own drawing, and knew their sizes in
advance. Allegedly. The code just assumes we are getting a size out of
somewhere.

But what if those classes did not know their sizes? What if we had to
pass them in our method calls? That's simple enough, anyways:
type
TRectangle = class(TObject)
public
procedure Draw(AWidth, AHeight: integer);
end;

// For now, let's assume squares draw the same way as rectangles.
TSquare = class(TRectangle);

The assumption in the above comment isn't necessarily correct. We can


identify a square with just one line length, not two - we can even identify it
by its perimeter, the length of its diagonals, or its surface area. A unique
rectangle, on the other hand, can only be identified if its width or height
are provided along with one of the above data points.

So how do we add Draw functions that accomplish this?

The obvious solution might be to declare new DrawSquareFromPerimeter or


DrawSquareFromDiagonal methods in TSquare . That's not really necessary, though,
and it's a little messy. Actually, if we wanted to implement all of the above
solutions, we would have to resort to that, or use some kind of additional
parameter to tell the program we're calculating from one specific
dimension. However, most of these Draw variants inevitably boil down to
calculating the height of the square before we start drawing, so we will go
with that approach.

The less obvious solution is to overload the method - redeclare it with a


different set of parameters. So that's what we're going to do:
TRectangle = class
public
// We don't have to make Draw a virtual method here,
// but we're going to do it anyway.
procedure Draw(AWidth, AHeight: integer); virtual;
end;

TSquare = class(TRectangle)
public
// This is why we made Draw virtual - to point out that
// overloading a virtual method will prompt the compiler
// to throw W1010 warnings at us again unless we reintroduce it.
// Overloading a static method doesn't
// require the 'reintroduce' keyword, though.
procedure Draw(ASize: integer); reintroduce; overload;
end;

Now, we can call both Draw variants on TSquare instances, so long as we


provide the right parameters:
var
Square: TSquare;
begin
Square := TSquare.Create;
// Will actually draw a rectangle. Oops.
// Maybe we need to override Draw and force it to sanitize its inputs?
Square.Draw(10, 5);
// Will draw a square using our overloaded Draw variant.
Square.Draw(10);
end.

Another way of dealing with the above situation is giving up on our TSquare
class completely, and adding both Draw methods to the TRectangle class.
Depending on which one we call, we would draw either a square or a
rectangle.
type
TRectangle = class
public
procedure Draw(AWidth, AHeight: integer); overload;
procedure Draw(ASize: integer); overload;
end;

var
Rectangle: TRectangle;

begin
Rectangle := TRectangle.Create;
Rectangle.Draw(10, 5);
Rectangle.Draw(10);
end.

4.4.10 Class methods

So far, we've been dealing with what is known as instance methods -


methods which operate on a specific object instance. However, we can
also define methods which operate on a whole class - these are known
as class methods:
type
TClassy = class
public
class var ClassLevel: integer;
class procedure DoSomethingClassy;
end;

class procedure TClassy.DoSomethingClassy;


begin
Inc(ClassLevel); // Increases ClassLevel by 1.
WriteLn('We''ve called a class method ', ClassLevel, ' times');
end;
There are a few differences between instance and class methods. For
instance, Self no longer refers to an object instance, but instead refers to
the class whose method is being called. Because of this, we can only use
Self to access other class methods and properties, or to access
constructors.

There are two ways we can call our DoSomethingClassy method above, both of
which will do the same thing. The only difference is in whether we are
calling it from an object instance or from the class itself:
var
Obj: TClassy;
begin
Obj := TClassy.Create;
Obj.DoSomethingClassy;
// Let's short-circuit the counter a bit, shall we?
TClassy.ClassLevel := 4;
TClassy.DoSomethingClassy;

The above example will output the following:


We've called a class method 1 times
We've called a class method 5 times

4.5 Resource management


Besides managing memory, programming requires managing other
system resources like file handles, communication sockets, or any other
system resource that requires releasing after being used. Failing to
release resources, or releasing them too late, leads to various problems
like crashes, deadlocks and an inability to perform certain operations.

While leaking small amounts of memory in non-critical applications that


do not have to run 24/7 does not have to be fatal for application
execution or performance, leaking much scarcer non-memory resources
will often prove critical sooner rather than later.

Having any kind of memory leaks, no matter how small they are, is
always a bad thing. The point is that occasionally leaking a few bytes of
memory might go unnoticed for a long time, while the issues caused by
draining system resources are way more prominent.
For instance, if an application creates or opens a file handle in exclusive
mode (usually to write something), and fails to release that handle when
it is done, it will not only block itself from doing anything else with that
particular file, but it will also prevent all other applications or the operating
system from using it.

In object oriented programming, resources are usually wrapped inside


objects. One example of such a wrapper class in Delphi is TFileStream .

4.5.1 RAII - Resource acquisition is initialization


One commonly used pattern in resource management, originating in
C++, is known as RAII - Resource acquisition is initialization.

What exactly is RAII?

It is a programming pattern where resource management is directly tied


with the lifetime of an object instance. In other words, a resource is
acquired - initialized - in the object's constructor and released in its
destructor. The acquired resource remains available and invariant during
that whole process. Since the resource and object instance are tightly
coupled, if there are no object - memory - leaks, there will be no resource
leaks, either.

The deterministic nature of Delphi's memory management systems - on


both compilers - is suitable for implementing that kind of resource
management. Even though the ARC compiler will release object
instances at a specific, known place and time, implicit hidden references
created by the compiler can fool us into thinking we are releasing an
instance at one place while they are actually released a few lines of code
later, in the method epilogue.

If the destruction order of the particular instance is critical, we have to


consider possible implicit references and ensure they cannot have a
negative impact on the destruction process.

Specifically, among others, RAII in Delphi is implemented in the


previously mentioned TFileStream class. In terms of the TFileStream class, a
somewhat simplified code example looks like this:
type
TFileStream = class(TStream)
protected
FHandle: THandle;
public
constructor Create(const AFileName: string; Mode: Word);
destructor Destroy; override;
end;

constructor TFileStream.Create(const AFileName: string; Mode: Word);


begin
inherited Create;
FHandle := FileOpen(AFileName, Mode);
if FHandle = INVALID_HANDLE_VALUE then
raise EFOpenError.Create('Cannot open file ' + AFileName);
end;

destructor TFileStream.Destroy;
begin
if FHandle <> INVALID_HANDLE_VALUE then
FileClose(FHandle);
inherited Destroy;
end;

The file resource is acquired during construction, and if that fails, so will
the constructor. The release of the acquired file resource will happen
inside the object instance's destructor, and not sooner.
var
fs: TFileStream;
begin
fs := TFileStream.Create('C:\foo.txt', fmCreate or fmShareExclusive);
try
fs.Write();
finally
fs.Free;
end;
end;

There is another particular thing to consider in relation to RAII and ARC


compiler: Reusing variables that can hold a lock on a particular resource.
Let's say you need to write something to some file stream and you need
to read that data back and you are doing it all in one procedure. You have
a single variable that can be used for both writing and reading part. In the
classic compiler you have to manually release an object instance, so you
have to explicitly call Free before you reopen the file and everything will
work as expected. The first file stream will be destroyed before the
second one is created and we would be able to open the file for reading.
var
fs: TFileStream;
begin
fs := TFileStream.Create('C:\foo.txt', fmCreate or fmShareExclusive);
try
fs.Write();
finally
fs.Free;
end;

fs := TFileStream.Create('C:\foo.txt', fmOpenRead or fmShareExclusive);


try
fs.Read();
finally
fs.Free;
end;
end;

In pure ARC code that does not have to work cross-platform, you may be
tempted to cut it down in the following manner:
var
fs: TFileStream;
begin
fs := TFileStream.Create('C:\foo.txt', fmCreate or fmShareExclusive);
fs.Write();

fs := TFileStream.Create('C:\foo.txt', fmOpenRead or fmShareExclusive);


fs.Read();
end;

Even though that code seems fine, it will fail for a rather simple reason.
The execution of the code during the assignment is not happening from
left to right, but from right to left - the constructor runs first and then the
newly constructed object is assigned to the reference, resulting in the
destruction of the previous object instance. In the above code, that
implies the construction of the new file stream while the existing one still
holds the lock, resulting in the second constructor's failure.

Even if we use separate variables for writing and reading, we would still
have to explicitly release the first file stream before we can create the
second one. And this is the kind of code where we have to think about
implicit references too. Such hidden strong references holding our first
file stream would interfere with that stream's destruction and break our
code. Assigning nil to the fs variable would not destroy the file stream at
that point as we desired:
var
fs: TFileStream;
begin
fs := TFileStream.Create('C:\foo.txt', fmCreate or fmShareExclusive);
fs.Write();
fs := nil;

fs := TFileStream.Create('C:\foo.txt', fmOpenRead or fmShareExclusive);


fs.Read();
end;
5 To be, or not to be
5.1 Null (nil) - The Billion Dollar Mistake
Sir Charles Antony Richard Hoare once referred to the implementation of
null references in ALGOL W (1965) as his billion dollar mistake and this
turned out to be the most quoted statement about null references and
how bad they are. One of the problems with quotes of any kind, is that
when taken out of context they tend to get misinterpreted and looked
upon from one side only. On one hand null references caused the costly
errors Sir Hoare referred to, but on the other hand they were inevitable.
In other words, if null did not exist, we would have to invent it.

One could say that null issues are more related to the lack of proper
language support for dealing with null than with null itself. While this may
seem like an oversight in language designs that failed to implement such
support, there is another problem - adding any type of checks and
specialized handling increases the complexity of compilers and the
resulting code - at the time that would be considered more of a luxury,
than a necessity.

Also, null was not a problem, until it obviously became a problem.

When you look at it, all the variety of languages we have and their
continuous evolution comes from the desire to figure out which are the
tasks at which humans are inherently bad, and what can be done to
move such tasks to the less fallible machine, thus making humans more
productive and more focused on the real work.

5.1.1 The purpose of nil

Nil represents a valid pointer or reference state. Nil is merely the absence
of a value, not a problem on its own. Our problems arise from the way we
deal with nil values. Just as zero plays an important role in math and you
can still crash your application if you divide by zero. The unexpected
usage of a nil reference represents a clear bug in code that needs to be
fixed just like any other bug we can encounter using or abusing any other
language feature.

Nil is the default value of a pointer or reference before you assign some
valid value to it - like assigning a newly created object instance. If you did
not have nil as a reliable flag that tells you whether your reference points
to a valid object or not, you would have to deal with something much
worse - wild pointers.

Nil is also used as the default value assigned to reusable references,


ones you temporarily release to save memory or resources, but can be
recreated later on demand. If you would not have the ability to use nil as
a flag value for such references, you would need an additional boolean
flag which would only add complexity to the program and increase the
possibility of having bugs.

5.1.2 Dealing with nil

You might think that the solution to the above would be creating a special
object that does nothing. So accessing nil would not crash your
application. Having this kind of nothing substitute for nil would just delay
our problem - accidental usage of a nil reference - because if you expect
nil and you forget to test for it, it is a bug in the program. If you don't
expect nil and you get it, it is a bug in the program.

"Do nothing" behavior would just replace those bugs with other ones. If
you expect a nothing reference and you forget to test for it, it is bug in the
program. If you don't expect nothing and you get it, it is a bug in the
program. Replacing those bugs that result in application crash, with an
equivalent bug that does not crash does not give you a properly working
application.

Some languages take dealing with nil to another level and provide
additional compile-time safety. Delphi does not have such capabilities,
but knowing about how and what kind of problems they solve can help in
better understanding why nil is or isn't so bad.

One of the problems with nil is that sometimes you want some references
to never, ever be nil. Ensuring that in Delphi is the developer's job - if you
fail, your application will crash at runtime. Some languages provide a
means of marking variables as non-nil, also called non-optional. Using
them moves responsibility from the developer to the compiler and
subsequently replaces having runtime errors with compile-time errors,
significantly reducing the potential for bugs.

Some languages also provide Optional/Nullable/Maybe types - they can


hold nil as a value, but when you use an optional, the compiler forces you
to test whether it contains a valid value or nil, along with plenty of
syntactic sugar that makes such checking easier. Since the compiler
prevents you from accidentally using nil values in optionals, that adds
another level of safety, preventing you from making silly mistakes.

There are three significant aspects of Optional/Nullable/Maybe types.


First is ability to hold nil (undefined) as a value - even for value types -
the second is being a signpost, communicating the intent that some
variable can hold an undefined value by design, and the third is
preventing accidental usage of those possible nil values. There is some
overlapping functionality across all those types in different languages and
also subtle differences in their implementation and safety features.

If you think the above can cover every case and you are home free,
never having to deal with unexpected nil references, think again.
Unchecked nil references are still possible even there. Completely
eradicating nil from a language is hard, and not always a viable option,
especially in general purpose languages and those that can get close to
the metal. But with proper and better language support it can be less of a
problem.

While Delphi does not have any nil safety capabilities built in, it is not so
bad either. Unlike some popular languages, managed (default) string
types in Delphi cannot hold nil and that greatly reduces potential nil
issues.

There are also some rules and patterns that can help in avoiding nil
reference problems, like using the null object pattern; protecting access
to lazy objects; limiting the scope of reference types; and creating object
instances at the very beginning of their scope and releasing them at the
end, making their lifetime as close as possible to the lifetime of the
reference they are stored in.

5.1.3 Avoiding nil at any cost can cost more

Nil is the cause of a certain range of problems, to be sure. But not all
languages suffer equally from it, and even in those languages that do
suffer more it is still quite an exaggerated problem. And that brings
another set of problems to the table.

In their desire to avoid nil at all cost, many developers will go to


extremes, making things worse. Some may write superfluous or unclear
code as well as force patterns borrowed from other languages that aren't
appropriate.

Don't do that. Delphi has nil as a value and not much compiler safety to
go with it. No amount of dancing around it will give you safety that only
changes to the language and compiler can bring. It will only make your
code more convoluted. Any time you deal with nil and your code starts to
look like a KISS violation, you are most likely doing it wrong.

Don't sprinkle your code with unnecessary nil checks. Check for nil only
those references where nil is an expected value by design, not those
where nil can occur by mistake. Use exception handling at appropriate
points to deal with mistakes and unpredictable errors.

Don't combine object references with any kind of boolean or other kind of
flags where a plain nil flag will suffice.

Don't abuse the lazy object pattern. If you are dealing with an object
instance that has to be available the whole time its reference is in scope,
then just create it at the first opportunity - when it enters the scope. Lazy
is not a safety belt replacement for trivial mistakes like forgetting to
construct the object at a certain point. Using lazy where it is not
appropriate obscures the code's intent.

Don't abuse the null object pattern. It is not a good fit for every scenario.
Use null object only when your code logic does not care whether it uses a
null object or a valid object instance. If you are merely replacing nil value
checks with checking whether your object is a null object, then you are
certainly misusing null object. Additionally, the lack of automatic memory
management in Delphi makes null object even less appropriate
compared to other languages that do have automatic memory
management.

5.1.4 Nil exception

Dereferencing - using - a nil pointer raises the following exception:


System.SysUtils.EAccessViolation

EAccessViolation is the exception class for invalid memory access


errors.

EAccessViolation is raised when an application:

Dereferences a nil (Delphi) or NULL (C++) pointer.


Writes to memory reserved for executable code.
Attempts to access a memory address for which there is no virtual
memory allocated to the application.

program NilException;

uses
System.SysUtils,
System.Classes;

type
TFoo = class(TObject)
public
First: integer;
Second: string;
end;

var
Foo: TFoo;

begin
Foo := nil;

try
Foo.Destroy;
except on E: Exception do
Writeln(E.Message);
end;

try
Writeln(Foo.First);
except on E: Exception do
Writeln(E.Message);
end;

try
Writeln(Foo.Second);
except on E: Exception do
Writeln(E.Message);
end;
end.

Running above program will output the following exceptions:


Access violation at address 0041B3EB in module 'NilException.exe'.
Read of address 00000000
Access violation at address 0041B444 in module 'NilException.exe'.
Read of address 00000004
Access violation at address 0041B4AF in module 'NilException.exe'.
Read of address 00000008

0041B3EB is address of the code where exception happened. If you are


running the application in the IDE, stop it at some breakpoint, and then
you can use the menus: Search -> Go to address -> type in address
prefixed with $ or 0x - $0041B3EB or 0x0041B3EB - and the cursor will
jump to the line belonging to the specified address, if that address
belongs to your application and not some external module.

Read of address 00000000, is the address of the memory location you


are trying to read. If that number is close to zero, you are most likely, but
not necessarily, accessing a nil reference. Address 00000000 points to
the object's VMT Virtual Method Table that is used to dispatch virtual
method calls, if the address is greater than 00000000 it will be the
address of some field. For instance, 00000004 is the address of First ,
and 00000008 is address of the Second field in the TFoo class compiled
when with a 32-bit compiler.

Remember, for larger classes that have many fields, close to zero does
not necessarily have to be really close. For example, the size of a TForm
object instance is larger than 900 bytes.

If the address you are trying to read is really large, something more like
408dc337 - that is an indication that you are accessing an invalid non-nil
pointer.

This is only a subset of possible access violation exception causes.


Discovering real memory corruption and other similar issues is much
harder to track down. Those usually require other debugging techniques
and sometimes need specialized debugging and logging tools.

Nevertheless, the ability to recognize and track down simple issues fast
can be quite a time saver.

5.1.5 How to test for nil

Delphi has two ways of testing for nil values. One is comparing reference
to nil with equality (or inequality) operators or using the intrinsic function
System.Assigned

function Assigned(var P): Boolean;

Tests for a nil (unassigned) pointer or procedural variable.

Use Assigned to determine whether the pointer or the procedure


referenced by P is nil. P must be a variable reference of a pointer or
procedural type.

Assigned(P) corresponds to the test P <> nil for a pointer variable, and
@P <> nil for a procedural variable.

Assigned returns False if P is nil, True otherwise.

As already noted, neither of those two options is capable of detecting


invalid (wild, dangling, stale) pointers and references. They are meant
only for checking valid references - including nil ones.
var
Obj: TObject;
begin
Obj := TObject.Create;
Obj.Free;
// following check will be True even though Obj is no longer valid object
if Assigned(Obj) then Writeln('Obj is assigned');
end.

=> Obj is assigned

When it comes to choosing between Assigned and a nil comparison, it does


not matter which one you use if you are dealing with regular pointer or
reference types. However, if you are dealing with any kind of procedural
variable, using Assigned is almost mandatory. Comparing to nil would also
work in some cases, but you cannot go wrong by using Assigned and
therefore using it is preferred.

What exactly is the problem with nil comparisons and procedural


variables?

First, you cannot compare a procedural variable directly, you have to


compare its address. The following code will fail to compile with the
following error when it encounters p <> nil

E2008 Incompatible types

type
TProcedure = procedure;

procedure Print;
begin
Writeln('Printing');
end;

var
p: TProcedure;

begin
p := Print;
if p <> nil then p;
end.
Replacing p with @p can be compiled and the comparison would work
properly, regardless of the value in p .
if @p <> nil then p;

So, what is the big deal? Typing @ is faster than typing Assigned and it is
also quite readable. And the compiler will not allow you to compile
syntactically incorrect code.

Well, not so fast... compiler protection is not something you can count on.
type
TObjectFunction = function: TObject;

function GetObject: TObject;


begin
Result := nil;
end;

var
f: TObjectFunction;

begin
f := GetObject;
if f <> nil then Writeln('f is not nil')
else Writeln('f is nil');
end.

Can you guess what will happen if you try running the above code? No, it
will not give you a compile-time error. It will compile perfectly. But the
output will be somewhat unexpected.
f is nil

How can f be nil when you assigned the GetObject function to it just one
line before???

Since there is no @ before the procedural variable f , the compiler will not
compare the contents of the variable with nil , it will call the function
stored in f and compare its result to nil instead. GetObject returns nil
yielding False in the if expression.

What is worse, if the content of the f variable is nil , the whole thing will
blow up with an access violation exception. Only because you forgot to
write one single @ .

And when you get back to the code, it is easy to miss the fact you are
missing @ . Life is too short to be wasted on such trivialities. Assigned is the
way to go!

If you are worried about the performance of Assigned , stop worrying. Assigned
is an intrinsic compiler function which means the compiler can replace
that function call with specialized, highly optimized code and does not
have to call any function at all. In fact, the generated code for @p <> nil
and Assigned(p) will be exactly the same, something along these lines:
if @p <> nil then p;
if Assigned(p) then p;

NilCheck.dpr.32: if @p <> nil then p;


00406126 833D9CAB400000 cmp dword ptr [$0040ab9c],$00
...

NilCheck.dpr.33: if Assigned(p) then p;


00406135 833D9CAB400000 cmp dword ptr [$0040ab9c],$00
...

5.2 Passing nil, returning nil


Passing nil and returning nil are commonly used coding patterns in
Delphi. There is nothing inherently bad with passing nil as a parameter
as well as returning it, as long as this is part of the design intent. Of
course, in such cases, appropriate nil checks must be put in place. If nil
is not an acceptable value, nothing special has to be done. If someone
passes nil by mistake, just let it crash.

Since Delphi does not have a default way of marking whether nil is an
acceptable value or not, before using any new library, framework or any
other code you are not familiar with, you have to read the documentation
first and find out what kind of values you can expect.

Using nil is fast, simple and flexible. If we look at all code used in some
application through a pyramid chart where the base consists of core
frameworks and reusable multipurpose code, and the peak is for highly
specialized application-specific code, then the code in the base of the
pyramid will be the code where nil will be used more, where using it
better caters to the different needs of the code built on top of it, and code
near the top is the code where nil can be replaced with other patterns,
where appropriate.

One of the most prominent use cases for passing nil is the TComponent
constructor. TComponent can maintain a hierarchy of owned objects. Each
component has an Owner property that points to its owner, another
TComponent . How do you define an owner for the object sitting at the top of
the hierarchy? Simply by passing nil to the constructor, indicating the
object has no owner. This could also be accomplished by having an
additional parameterless constructor. However, maintaining a single
virtual constructor simplifies the construction of different TComponent
descendants via metaclasses, as used in Delphi's component streaming
system.

Another highly used pattern for passing nil is using setter methods for
some optional collaborator objects, where assigning nil to a property is
redirected to a setter behind the scenes. Similarly to setter methods,
getter methods for such properties can also use nil as a valid value and
return it.

As far as regular functions returning nil are concerned, one of the use
cases is locating specific objects in some collection. If an object
conforming to the required parameters is not found, nil can be returned.

One of the exceptions for returning nil is returning an array or collection


of values, where using an empty array or empty collection is usually more
appropriate than returning nil .

However, since Delphi does not have automatic memory management,


all object instances returned from some function must utilize the same
memory management. The receiver of the object instance either does not
take ownership and responsibility for an instance and its release or it
does take ownership and responsibility for releasing the instance,
regardless of the instance's content. From that standpoint, an empty
collection can be returned if and only if such a collection would have the
same ownership treatment as any non-empty collection returned.
5.3 Nil (Null) Object Pattern
The null object pattern is a safer replacement for nil object references. It
is basically a class that implements the required object interface (API) but
it does nothing.

We replaced crashing with a do nothing behavior. Sounds like a good


trade, doesn't it? Well, not always.

If we continuously need to test whether we are dealing with a null object


and change the application flow accordingly, then we are abusing the null
object pattern and should be dealing with nil values on their own.

As has already been said, used improperly the null object pattern just
shifts potential problems to another level. If we have a crashing
application because we mistreated nil value, then the mere introduction
of null object will not magically solve our code logic. It will merely replace
crashing with some other form of buggy behavior that can be even harder
to find than the bugs manifested by an ordinary crash. However, that
does not mean null object is useless everywhere, it merely points out that
null object is not a universal solution.

5.3.1 A use case for null object

Null object is useful in places where you don't care whether you deal with
a null object or a valid object instance. For example, when you have
some optional collaborator objects, that have to perform some task,
replaceable with do nothing behavior.

One of the examples would be a logger class. Using a do nothing logger


when you want to disable logging is simpler than constant checks
whether the logger is assigned or not.

The following is a simple logging example.

The TLogger class is an abstract class that defines a logging API. TNullLogger
is declared as a separate class to give us a clear idea how to implement
null object and do nothing behavior, and TConsoleLogger is a logger
implementation that actually does something.
Instead of having an abstract base TLogger class we could start with a non-
abstract TLogger class that would implement do nothing behavior by default
and avoid separate TNullLogger , but separating the base API (interface)
declaration from different implementations, including the null one, is
clearer in intent and purpose of each and every class.
program NullObjectPattern;

uses
System.SysUtils,
System.Classes;

type
TLogger = class(TObject)
public
procedure Log(const Msg: string); virtual; abstract;
end;

TNullLogger = class(TLogger)
public
procedure Log(const Msg: string); override;
end;

TConsoleLogger = class(TLogger)
public
procedure Log(const Msg: string); override;
end;

TRocket = class(TObject)
protected
Logger: TLogger;
public
constructor Create(ALogger: TLogger);
procedure Design;
procedure Build;
procedure Launch;
procedure Go;
end;

procedure TNullLogger.Log(const Msg: string);


begin
// do nothing
end;

procedure TConsoleLogger.Log(const Msg: string);


begin
Writeln('Log: ', Msg);
end;
constructor TRocket.Create(ALogger: TLogger);
begin
inherited Create;
Logger := ALogger;
end;

procedure TRocket.Design;
begin
Logger.Log('Designing');
// designing code goes here
end;

procedure TRocket.Build;
begin
Logger.Log('Building');
// rocket building code goes here
end;

procedure TRocket.Launch;
begin
Logger.Log('Launching');
// whatever you need to launch the rocket
Logger.Log('Looking good');
end;

procedure TRocket.Go;
begin
Logger.Log('Going to the Moon');
Design;
Build;
Launch;
Logger.Log('See you there...');
end;

var
SomeLogger: TLogger;
Rocket: TRocket;

begin
SomeLogger := TConsoleLogger.Create;
try
Rocket := TRocket.Create(SomeLogger);
try
Rocket.Go;
finally
Rocket.Free;
end;
finally
SomeLogger.Free;
end;
end.

If we create a Rocket instance passing a TConsoleLogger object we will get the


following output. However, if we use TNullLogger instead, no logging would
be performed.
Log: Going to the Moon
Log: Designing
Log: Building
Log: Launching
Log: Looking good
Log: See you there...

We can change the behavior from the outside, without changing TRocket 's
inner workings. Without the null object pattern we could achieve the
same, allowing Logger to be nil and then do nil checks every time we want
to log something. However, in this case using a null object makes more
sense. It prevents access violation exceptions if we forget a nil check
before using Logger , and at the same time, it makes the code more
readable without any negative side effects.

For instance, a Launch method that can operate on a nil Logger would have
to be updated in the following manner:
procedure TRocket.Launch;
begin
if Assigned(Logger) then Logger.Log('Launching');
// whatever you need to launch the rocket
if Assigned(Logger) then Logger.Log('Looking good');
end;

5.3.2 A case against null object

As already mentioned, null object is suitable for implementing do nothing


behavior, where consumer code logic is the same regardless of whether
the collaborating object is nil or not. Merely replacing nil checks for a null
object check is a clear sign that the null object pattern is not an
appropriate one.

One of the issues with the null object pattern is that you have to provide a
specialized null object class that will implement do nothing behavior, and
often a single null object instance. In our logger example, maintaining a
single null logger instance is not necessary. Regardless of which logger
class you use, there will be either one or very few instances required. On
the other hand, using the null object pattern in some tree-like structure
with many leaf nodes, maintaining a single null node instance that can
serve as an endpoint for all leaves is imperative.

This makes the null object pattern more appropriate for automatic
management systems, since you don't have to manually release objects.
Under manual memory management, you have to be careful not to
release your null object instance, and protecting that instance from
accidental release can be as hard as protecting accidental access to nil .

Different implementations of a simple Binary Search Tree can give us a


clear picture why using null object is not always the best idea.

The first implementation uses plain nil as leaf objects and will serve as a
base for building null object implementations. All implementations will
count the number of instantiated nodes, for later comparison.
program TreeNil;

var
Count: Integer;

type
TNode = class(TObject)
strict private
FValue: Integer;
FLeft, FRight: TNode;
public
constructor Create(AValue: Integer);
destructor Destroy; override;
property Value: Integer read FValue;
property Left: TNode read FLeft write FLeft;
property Right: TNode read FRight write FRight;
end;

TBinarySearchTree = class(TObject)
protected
function DoSearch(AValue: Integer; ANode: TNode): TNode;
procedure DoPrint(ANode: TNode);
public
Root: TNode;
destructor Destroy; override;
procedure Insert(AValue: Integer);
function Search(AValue: Integer): TNode;
procedure Print;
end;

constructor TNode.Create(AValue: Integer);


begin
Inc(Count);
FValue := AValue;
end;

destructor TNode.Destroy;
begin
FLeft.Free;
FRight.Free;
inherited;
end;

destructor TBinarySearchTree.Destroy;
begin
Root.Free;
inherited;
end;

procedure TBinarySearchTree.Insert(AValue: Integer);


var
Node, Current, Parent: TNode;
begin
Node := TNode.Create(AValue);

if Root = nil then Root := Node


else
begin
Current := Root;
while true do
begin
Parent := Current;
if AValue < Parent.Value then
begin
Current := Current.Left;
if Current = nil then
begin
Parent.Left := Node;
break;
end;
end
else
begin
Current := Current.Right;
if Current = nil then
begin
Parent.Right := Node;
break;
end;
end;
end;
end;
end;

function TBinarySearchTree.DoSearch(AValue: Integer; ANode: TNode): TNode;


begin
if (ANode = nil) or (AValue = ANode.Value) then Result := ANode
else
if AValue < ANode.Value then Result := DoSearch(AValue, ANode.Left)
else Result := DoSearch(AValue, ANode.Right);
end;

function TBinarySearchTree.Search(AValue: Integer): TNode;


begin
Result := DoSearch(AValue, Root);
end;

procedure TBinarySearchTree.DoPrint(ANode: TNode);


begin
if ANode <> nil then
begin
DoPrint(ANode.Left);
Writeln(ANode.Value);
DoPrint(ANode.Right);
end;
end;

procedure TBinarySearchTree.Print;
begin
DoPrint(Root);
end;

var
Tree: TBinarySearchTree;

begin
ReportMemoryLeaksOnShutdown := true;

Tree := TBinarySearchTree.Create;
try
Tree.Insert(6);
Tree.Insert(5);
Tree.Insert(3);
Tree.Insert(2);
Tree.Insert(9);
Tree.Insert(4);
Tree.Insert(7);
Tree.Insert(8);
Tree.Insert(1);
Tree.Print;

if Tree.Search(10) = nil then Writeln('10 not found')


else Writeln('10 found');
finally
Tree.Free;
end;

Writeln('Total count: ', Count);


end.

Running our base BST implementation will yield the following output:
1
2
3
4
5
6
7
8
9
10 not found
Total count: 9

As we can see, our BST works perfectly, properly sorted. We can also
test whether some value is stored in the BST or not. All other
implementations must provide the same output. Then the only thing that
can vary is the Total count representing the number of node object
instances we have created. For a nil -based BST that is exactly the
number of values we added to the tree. There is no overhead when it
comes to creating nodes, because our leaf nodes are plain nil flags.

Our first null object BST implementation will construct a separate null
node for each endpoint.
program TreeNullObject;

var
Count: Integer;

type
TNode = class(TObject)
strict protected
FValue: Integer;
FLeft, FRight: TNode;
function GetLeft: TNode; virtual;
function GetRight: TNode; virtual;
procedure SetLeft(const Value: TNode);
procedure SetRight(const Value: TNode);
public
constructor Create(AValue: Integer);
destructor Destroy; override;
function IsNull: boolean; virtual;
property Value: Integer read FValue;
property Left: TNode read GetLeft write SetLeft;
property Right: TNode read GetRight write SetRight;
end;

TNullNode = class(TNode)
strict protected
function GetLeft: TNode; override;
function GetRight: TNode; override;
public
constructor Create;
function IsNull: boolean; override;
end;

TBinarySearchTree = class(TObject)
protected
function DoSearch(AValue: Integer; ANode: TNode): TNode;
procedure DoPrint(ANode: TNode);
public
Root: TNode;
constructor Create;
destructor Destroy; override;
procedure Insert(AValue: Integer);
function Search(AValue: Integer): TNode;
procedure Print;
end;

constructor TNode.Create(AValue: Integer);


begin
Inc(Count);
FValue := AValue;
FLeft := TNullNode.Create;
FRight := TNullNode.Create;
end;

destructor TNode.Destroy;
begin
FLeft.Free;
FRight.Free;
inherited;
end;

function TNode.GetLeft: TNode;


begin
Result := FLeft;
end;

function TNode.GetRight: TNode;


begin
Result := FRight;
end;

function TNode.IsNull: boolean;


begin
Result := false;
end;

procedure TNode.SetLeft(const Value: TNode);


begin
if FLeft.IsNull then FLeft.Free;
FLeft := Value;
end;

procedure TNode.SetRight(const Value: TNode);


begin
if FRight.IsNull then FRight.Free;
FRight := Value;
end;

constructor TNullNode.Create;
begin
Inc(Count);
end;

function TNullNode.GetLeft: TNode;


begin
Result := Self;
end;

function TNullNode.GetRight: TNode;


begin
Result := Self;
end;

function TNullNode.IsNull: boolean;


begin
Result := true;
end;
constructor TBinarySearchTree.Create;
begin
Root := TNullNode.Create;
end;

destructor TBinarySearchTree.Destroy;
begin
Root.Free;
inherited;
end;

procedure TBinarySearchTree.Insert(AValue: Integer);


var
Node, Current, Parent: TNode;
begin
Node := TNode.Create(AValue);

if Root.IsNull then
begin
Root.Free;
Root := Node;
end
else
begin
Current := Root;
while true do
begin
Parent := Current;
if AValue < Parent.Value then
begin
Current := Current.Left;
if Current.IsNull then
begin
Parent.Left := Node;
break;
end;
end
else
begin
Current := Current.Right;
if Current.IsNull then
begin
Parent.Right := Node;
break;
end;
end;
end;
end;
end;
function TBinarySearchTree.DoSearch(AValue: Integer; ANode: TNode): TNode;
begin
if ANode.IsNull or (AValue = ANode.Value) then Result := ANode
else
if AValue < ANode.Value then Result := DoSearch(AValue, ANode.Left)
else Result := DoSearch(AValue, ANode.Right);
end;

function TBinarySearchTree.Search(AValue: Integer): TNode;


begin
Result := DoSearch(AValue, Root);
end;

procedure TBinarySearchTree.DoPrint(ANode: TNode);


begin
if not ANode.IsNull then
begin
DoPrint(ANode.Left);
Writeln(ANode.Value);
DoPrint(ANode.Right);
end;
end;

procedure TBinarySearchTree.Print;
begin
DoPrint(Root);
end;

var
Tree: TBinarySearchTree;

begin
ReportMemoryLeaksOnShutdown := true;

Tree := TBinarySearchTree.Create;
try
Tree.Insert(6);
Tree.Insert(5);
Tree.Insert(3);
Tree.Insert(2);
Tree.Insert(9);
Tree.Insert(4);
Tree.Insert(7);
Tree.Insert(8);
Tree.Insert(1);
Tree.Print;

if Tree.Search(10).IsNull then Writeln('10 not found')


else Writeln('10 found');
finally
Tree.Free;
end;

Writeln('Total Count: ', Count);


end.

And our second null object implementation of a Binary Search Tree will
use a common, single null node instance.
program TreeNullObjectSingle;

var
Count: Integer;

type
TNode = class(TObject)
strict protected
FValue: Integer;
FLeft, FRight: TNode;
function GetLeft: TNode; virtual;
function GetRight: TNode; virtual;
procedure SetLeft(const Value: TNode);
procedure SetRight(const Value: TNode);
public
constructor Create(AValue: Integer);
destructor Destroy; override;
function IsNull: boolean; virtual;
property Value: Integer read FValue;
property Left: TNode read GetLeft write SetLeft;
property Right: TNode read GetRight write SetRight;
end;

TNullNode = class(TNode)
public
class var Instance: TNode;
class constructor ClassCreate;
class destructor ClassDestroy;
strict protected
function GetLeft: TNode; override;
function GetRight: TNode; override;
public
constructor Create;
function IsNull: boolean; override;
end;

TBinarySearchTree = class(TObject)
protected
function DoSearch(AValue: Integer; ANode: TNode): TNode;
procedure DoPrint(ANode: TNode);
public
Root: TNode;
constructor Create;
destructor Destroy; override;
procedure Insert(AValue: Integer);
function Search(AValue: Integer): TNode;
procedure Print;
end;

constructor TNode.Create(AValue: Integer);


begin
Inc(Count);
FValue := AValue;
FLeft := TNullNode.Instance;
FRight := TNullNode.Instance;
end;

destructor TNode.Destroy;
begin
if not Left.IsNull then FLeft.Free;
if not Right.IsNull then FRight.Free;
inherited;
end;

function TNode.GetLeft: TNode;


begin
Result := FLeft;
end;

function TNode.GetRight: TNode;


begin
Result := FRight;
end;

function TNode.IsNull: boolean;


begin
Result := false;
end;

procedure TNode.SetLeft(const Value: TNode);


begin
FLeft := Value;
end;

procedure TNode.SetRight(const Value: TNode);


begin
FRight := Value;
end;
class constructor TNullNode.ClassCreate;
begin
Instance := TNullNode.Create;
end;

class destructor TNullNode.ClassDestroy;


begin
Instance.Free;
end;

constructor TNullNode.Create;
begin
Inc(Count);
end;

function TNullNode.GetLeft: TNode;


begin
Result := Self;
end;

function TNullNode.GetRight: TNode;


begin
Result := Self;
end;

function TNullNode.IsNull: boolean;


begin
Result := true;
end;

constructor TBinarySearchTree.Create;
begin
Root := TNullNode.Instance;
end;

destructor TBinarySearchTree.Destroy;
begin
if not Root.IsNull then Root.Free;
inherited;
end;

procedure TBinarySearchTree.Insert(AValue: Integer);


var
Node, Current, Parent: TNode;
begin
Node := TNode.Create(AValue);

if Root.IsNull then Root := Node


else
begin
Current := Root;
while true do
begin
Parent := Current;
if AValue < Parent.Value then
begin
Current := Current.Left;
if Current.IsNull then
begin
Parent.Left := Node;
break;
end;
end
else
begin
Current := Current.Right;
if Current.IsNull then
begin
Parent.Right := Node;
break;
end;
end;
end;
end;
end;

function TBinarySearchTree.DoSearch(AValue: Integer; ANode: TNode): TNode;


begin
if ANode.IsNull or (AValue = ANode.Value) then Result := ANode
else
if AValue < ANode.Value then Result := DoSearch(AValue, ANode.Left)
else Result := DoSearch(AValue, ANode.Right);
end;

function TBinarySearchTree.Search(AValue: Integer): TNode;


begin
Result := DoSearch(AValue, Root);
end;

procedure TBinarySearchTree.DoPrint(ANode: TNode);


begin
if not ANode.IsNull then
begin
DoPrint(ANode.Left);
Writeln(ANode.Value);
DoPrint(ANode.Right);
end;
end;

procedure TBinarySearchTree.Print;
begin
DoPrint(Root);
end;

var
Tree: TBinarySearchTree;
// code using single null object implementation
// is the same as code using multiple one
...

Let's take a look at the code and compare the differences. But before we
start looking at the code, we should look at the memory overhead.

For n nodes added to the tree, the total number of instantiated objects is:

BST implementation Node instances


nil 9 (n)
multiple null objects 28 (2n + 1)
single null object 10 (n + 1)

The difference between the number of instantiated object instances in the


nil check and single null object implementations is insignificant - it
consists only of a single extra object instance representing our null
object. However, the multiple null object implementation clearly shows an
inflation of null object nodes, making this implementation completely
unsuitable for any real-life usage.

The real comparison here is between the nil and single null object BST
implementations.

Besides having the additional TNullNode class, our base TNode class
declaration is more complex. In order to implement the do nothing
behavior in the descendant TNullNode class, instead of plain field access to
the Left and Right nodes, we had to implement virtual getters and setters.
Also, we need the IsNull function, which will tell us which kind of node we
are dealing with.

Our constructor has more work to do. Besides assigning the stored value,
we also have to assign a shared null node instance. Destruction of the
null nodes also brings another level of complexity.
Now, let's see if all that additional work is justified. Did we get any real
safety? Have we simplified the code that uses null nodes?

Absolutely not.

In all the code that deals with possible null nodes we have replaced nil
checks with IsNull checks. If we forget the IsNull check in the DoPrint
method we are trading an access violation exception for a stack overflow
exception. Overall, our internal BST code is no less error-prone or
simpler. On the contrary, it is more convoluted and brings only a false
sense of security, as it feels like we have strengthened our code when we
actually made no improvement whatsoever.

Further profiling shows that the null object implementation is also about
twice as slow as the original one.

5.4 Nullable types


The nil value as a flag for marking references that hold no valid object
instance is so valuable and commonly used in everyday programming
that the similar concept of nullable types is invented for value types that
organically cannot hold nil or a similar undefined value.

It is hard to talk about nil without mentioning nullable types, though


deeper coverage may seem out of place in a memory management book
with a primary focus on managing object instances. But digressing a bit
would not hurt anyone, right? Besides, the implementation of nullable
types will use properties of managed types - namely interfaces - for
achieving its final goal. So in a way it is related to memory management
after all.

While interfaces and their functionality are covered later on, it makes
sense to cover nullable types under the Nil chapter. It is very difficult to
present these issues in a linear fashion, and you may need to jump
around a bit, depending on your prior experience.

However, it is important to remember that Delphi does not give you


compile-time safety when dealing with nullable types. Delphi's nullable
implementation only gives you the ability to add an extra flag to value
types indicating they don't hold a valid value at a certain point.

The following implementation is based on A "Nullable" Post by Allen


Bauer Actually, it is most likely an exact copy... or maybe I changed it a
bit over time... but I can't remember and it does not matter in the end.

You cannot change the base concept of a hard-coded interface, and the
rest of the code is simple enough. While there may be slight variations,
most implementations look rather similar at their core.

There are also some implementations where a string as an auto-


managed type is used instead of a fake interface in the FHasValue field.
However, a fake interface is faster and has a smaller memory footprint.

To implement a nullable type, we need to create a wrapper around a


value type and add an undefined flag. That part can be easily achieved
with a generic record storing our wrapped Value and HasValue flag.

Most of the TNullable<T> implementation is rather obvious. Create is used to


set Value when we have some actual value we want to store. Value and
ValueOrDefault are used for retrieval of a stored value or returning some
default value if there is nothing stored, and custom operators are used to
simplify handling.

One thing that sticks out is the previously mentioned FHasValue field that is
declared as an interface and not a plain boolean flag. This is solely due
to the fact that Delphi does not have automatic initialization of records.
Without default automatic initialization, our boolean flag might start its life
with any random value - it can be False and it can be True . To avoid error-
prone manual initialization of a nullable record, we are exploiting the fact
that Delphi always initializes managed fields. Using an interface instead
of a boolean will give us automatic initialization and our nullable record
will always start its life properly initialized - HasValue will always be false,
until we actually store some value inside.

More about the fake interface mechanism and how it actually works can
be found in the Interfaces chapter.
unit Nullable;
interface

uses
System.SysUtils,
System.Classes,
System.Generics.Defaults;

type
TNullable<T> = record
strict private
FValue: T;
FHasValue: IInterface;
function GetValue: T;
function GetHasValue: boolean;
public
constructor Create(AValue: T);
function ValueOrDefault: T; overload;
function ValueOrDefault(Default: T): T; overload;
property HasValue: boolean read GetHasValue;
property Value: T read GetValue;

class operator Implicit(Value: TNullable<T>): T;


class operator Implicit(Value: T): TNullable<T>;
class operator Explicit(Value: TNullable<T>): T;
class operator NotEqual(const Left, Right: TNullable<T>): boolean;
class operator Equal(const Left, Right: TNullable<T>): boolean;
end;

procedure SetFakeInterface(var Intf: IInterface);

implementation

function NopAddRef(inst: Pointer): integer; stdcall;


begin
Result := -1;
end;

function NopRelease(inst: Pointer): integer; stdcall;


begin
Result := -1;
end;

function NopQueryInterface(inst: Pointer; const IID: TGUID; out Obj):


HResult; stdcall;
begin
Result := E_NOINTERFACE;
end;

const
FakeInterfaceVTable: array [0 .. 2] of Pointer =
(@NopQueryInterface, @NopAddRef, @NopRelease);
FakeInterfaceInstance: Pointer = @FakeInterfaceVTable;

procedure SetFakeInterface(var Intf: IInterface);


begin
Intf := IInterface(@FakeInterfaceInstance);
end;

constructor TNullable<T>.Create(AValue: T);


begin
FValue := AValue;
SetFakeInterface(FHasValue);
end;

function TNullable<T>.GetHasValue: boolean;


begin
Result := FHasValue <> nil;
end;

function TNullable<T>.GetValue: T;
begin
if not HasValue then
raise Exception.Create('Invalid operation, Nullable type has no value');
Result := FValue;
end;

function TNullable<T>.ValueOrDefault: T;
begin
if HasValue then Result := fValue
else Result := default(T);
end;

function TNullable<T>.ValueOrDefault(Default: T): T;


begin
if not HasValue then Result := default
else Result := fValue;
end;

class operator TNullable<T>.Explicit(Value: TNullable<T>): T;


begin
Result := Value.Value;
end;

class operator TNullable<T>.Implicit(Value: TNullable<T>): T;


begin
Result := Value.Value;
end;

class operator TNullable<T>.Implicit(Value: T): TNullable<T>;


begin
Result := TNullable<T>.Create(Value);
end;

class operator TNullable<T>.Equal(const Left, Right: TNullable<T>): boolean;


var
Comparer: IEqualityComparer<T>;
begin
if Left.HasValue and Right.HasValue then
begin
Comparer := TEqualityComparer<T>.Default;
Result := Comparer.Equals(Left.Value, Right.Value);
end
else Result := Left.HasValue = Right.HasValue;
end;

class operator TNullable<T>.NotEqual(const Left, Right: TNullable<T>): boolean;


var
Comparer: IEqualityComparer<T>;
begin
if Left.HasValue and Right.HasValue then
begin
Comparer := TEqualityComparer<T>.Default;
Result := not Comparer.Equals(Left.Value, Right.Value);
end
else Result := Left.HasValue <> Right.HasValue;
end;

end.

We have a Nullable<T> implementation. Now what? How do we put it to


good use?

One of the advantages of value types is they cannot hold nil . They
always have a value, not always a meaningful one, but it seems that
people would rather deal with bugs caused by random values than nil
exceptions. The belief that nil is a horrible, horrible invention is so
widespread that finding a proper undisputable use case for Nullable is not
an easy task.

Imagine some weather station that measures some weather-related data


and sends a report every hour. To make it simple, let's measure only two
things: temperature and wind speed. To make it even simpler, we will use
integer types to represent those values.
type
TWeatherReport = record
public
Hour: Integer;
Temperature: Integer;
Wind: Integer;
procedure Print;
end;

procedure TWeatherReport.Print;
begin
Write('Hour: ', Hour);
Write(', Temp: ', Temperature);
Writeln(', Wind: ', Wind);
end;

In this case, we are only interested in displaying a weather report, not


how we get actual numbers filled into the report. So, let's fill some data
and print the report.
var
Report: TWeatherReport;

begin
Report.Hour := 10;
Report.Temperature := 20;
Report.Wind := 5;

Report.Print;
end.

Great, everything works just fine. Why on Earth would we need any
Nullable here?
Hour: 10, Temp: 20, Wind: 5

If only it were that simple. Something happened and our station's


thermometer broke. We have to fill the report and send the data, but what
value do we write in the Temperature field? We can certainly have values
below and above zero, as well as zero itself. If we put aside the nitpicking
response, that we might use any value below absolute zero as an
impossible value, there is just no appropriate integer value we can use.

Since we are bounded by integers, there is no other way than to use -274
degrees Celsius as an impossible value. And we happily fill our report
and print it.
Report.Hour := 11;
Report.Temperature := -274;
Report.Wind := 4;

Report.Print;

Hour: 11, Temp: -274, Wind: 4

And before you know it, you start getting calls from people thinking we
are looking at a new ice age.

Turns out, -274 was not the greatest value after all. But let's not blame
the value. We can adjust our code. If the Temperature is -274 we would not
print it. We can do same with the Wind field - if the value is -1, we didn't
measure it.
procedure TWeatherReport.Print;
begin
Write('Hour: ', Hour);
if Temperature = -274 then Write(', Temp: not measured')
else Write(', Temp: ', Temperature);
if Wind = -1 then Writeln(', Wind: not measured')
else Writeln(', Wind: ', Wind);
end;

So far, so good. Everything works now... except for the thermometer...


but, lunch comes first.
Report.Hour := 12;
Report.Temperature := -274;
Report.Wind := 4;

Report.Print;

Hour: 12, Temp: not measured, Wind: 4

During lunch hour, our coworker decides to put -300. For good measure.
And there goes the ice age again.
Report.Hour := 13;
Report.Temperature := -300;
Report.Wind := 4;

Report.Print;

Hour: 13, Temp: -300, Wind: 4


Who wrote that stupid code? Anything below absolute zero should count
as not measured. Fixed.
procedure TWeatherReport.Print;
begin
Write('Hour: ', Hour);
if Temperature <= -274 then Write(', Temp: not measured')
else Write(', Temp: ', Temperature);
if Wind <= -1 then Writeln(', Wind: not measured')
else Writeln(', Wind: ', Wind);
end;

Until the night shift comes in. Absolute zero? Piece of cake -269.
Hour: 23, Temp: -269, Wind:6

By now, you are probably convinced that having some arbitrary value as
a flag is a rather error-prone solution.

This is where Nullable comes in. If we declare the Temperature and Wind
fields as TNullable<Integer> , and adapt our Print procedure we have solved
the problem. If we cannot write an actual measured value, we just don't
use any. Nil clearly describes no value, undefined value intent, both for
developers writing and reading the code as well as for people using the
application.
type
TWeatherReport = record
public
Hour: Integer;
Temperature: TNullable<Integer>;
Wind: TNullable<Integer>;
procedure Print;
end;

procedure TWeatherReport.Print;
begin
Write('Hour: ', Hour);
if Temperature.HasValue then Write(', Temp: ', Temperature.Value)
else Write(', Temp: not measured');
if Wind.HasValue then Writeln(', Wind: ', Wind.Value)
else Writeln(', Wind: not measured');
end;

Report.Hour := 23;
Report.Wind := 6;
Report.Print;

Ice age problem solved. Now if only we could get rid of global warming
that easily...
6 Ownership
There are two aspects of ownership. The first one is strictly about the
relationship between object instances, and the second is about a
responsibility to release an object instance when it is no longer needed.
Regardless of the aspect, references to object instances play a
significant part in ownership models as they define both aspects -
relationships and responsibilities.

While automatic memory management systems don't make a distinction


between the two, manual memory management takes ownership in a
more relaxed way and subsequently allows coding patterns that can get
you in trouble. On the other hand, manual memory management also
allows implementing mechanisms where you can safely ignore the
ownership rules and release an object instance owned by someone else -
safely, as long as the owner is notified about such an event.

6.0.1 Owning references

Creating an object instance also creates responsibility: The responsibility


to release that instance by calling its destructor when it is no longer
needed. This is where references come into the picture. They provide an
access point to our object instance and the initial reference, implicit or
explicit, is also the owner of an object instance. The owner of an instance
is responsible for its release, unless it transfers ownership to someone
else - some other reference. This is the meaning of the owning reference
expression.

Besides providing an access point to object instance, owning references


also provide an access point to the related object graph through the
contained owning references. Convenience references that merely point
back to any instance in the object graph, or to sibling object instances,
are non-owning ones.

6.0.2 Owner
The ownership relation between different objects is expressed through
owning references contained in an object that points to other object
instances. An object that contains an owning reference to another object
is called an owner.

6.0.3 Ownership transfer

Every owning reference can transfer ownership of the object instance to


some other reference. When the transfer is successfully completed, the
original owning reference should be discarded and must not be used for
releasing that object instance. Unless the ownership transfer is a trivial
reference assignment, it can result in exceptions where we have to
properly release an object instance that is stuck in the process and is still
owned by the original reference. However, this is only a concern in
manual memory management with non-reference counted instances. All
reference counted instances will be properly released by the counting
mechanism.

Ownership comes in two acceptable flavors: exclusive and shared


ownership. Anything else leads to pure chaos. Regardless of the flavor,
every object instance must have at least one owning reference at any
time. Otherwise, it cannot be used because it cannot be reached through
code.

6.0.4 Exclusive ownership

Every object instance must have one and only one owning reference
at any time. Following that rule is of the utmost importance under manual
memory management. This is the best way we can avoid premature
releases or double releases of our object instance.

The previously mentioned model where a non-owning reference can


safely release the object should be actually taken as exclusive ownership
where we have a mechanism capable of performing ownership transfer
during the instance destruction process.

6.0.5 Shared ownership


There is an exception to the above rule - shared ownership, where object
instances can have more than one owning reference.

What is the difference between shared ownership and pure chaos?

It is in a responsibility to track all owning references so we can ensure


that the object will not be released while it is in use, and ensure that we
will release the instance when the last owning reference goes out of
scope. This kind of instance sharing is easily achieved via reference
counting.
7 Object Instance Lifecycle
There are three stages in the lifecycle of every object instance:

1. construction - creation
2. active lifetime
3. destruction

From a memory management point of view, we are interested in the first


and the last one. There are two specialized kinds of methods at the root
of every object construction and destruction process: the constructor and
destructor methods, denoted with the equivalent reserved words, and
commonly referred to as ctor and dtor.

When the compiler runs into code that calls a constructor like
TSomething.Create , or a destructor such as SomeObject.Destroy , it will not simply
call those methods like it would do with any other method, but will insert a
rather elaborate code sequence behind the scenes.

Construction of an object involves the following stages:

1. memory allocation - _GetMem


2. memory initialization - InitInstance
3. executing the appropriate chain of constructors
4. executing AfterConstruction chain

Destruction of an object instance has the following stages:

1. executing BeforeDestruction chain


2. executing the appropriate chain of destructors
3. executing CleanupInstance to clean up and finalize managed-type fields -
strings, dynamic arrays, variants, interfaces...
4. memory deallocation - _FreeMem

The following diagram depicts the whole lifecycle and all the important
steps that happen during the construction and deconstruction stages.
Delphi is an exception-safe language. This means that properly handled
exceptions during construction can and will leave the application in a fully
operational state.
7.1 Difference in object instance lifecycle between
classic and ARC compiler
The basic workflow for object instance construction and destruction is the
same in classic and ARC compilers. There are some differences in the
actual code executed due to the reference counting mechanism in the
ARC compiler, but all object instances will always go through the same
four construction and destruction stages.

However, there is one important difference that will be covered in more


detail in the DisposeOf chapter later in the book.

Under the classic compiler, all construction stages will execute as one
sequential block; all destruction stages will also execute as a block. The
ARC compiler has the same behavior for object construction, but its
object destruction behavior depends on whether or not DisposeOf has been
called on the object instance. If destruction is triggered without involving
DisposeOf , it will execute as one block just like it would under the classic
compiler. If DisposeOf is called, stages 1 and 2 will be executed immediately
as one block, and stages 3 and 4 as another that will be executed at later
time - introducing some side-effects.

7.2 Exceptions and exception handling in lifecycle


methods
Delphi has three behavior contracts - rules - that must be strictly followed
to ensure proper memory handling, avoid memory leaks and leave the
application in an operational state.

1. Construction of an object instance can throw exceptions


2. The destruction process must not throw exceptions
3. Destructors and other related methods must be capable of cleaning up
partially constructed instances

7.2.1 Construction of an object instance can throw exceptions


We have to expect that an exception can be thrown at any stage in the
object construction process. There is no such thing as safe object
construction. If nothing else, EOutOfMemory can be raised if we run out of
memory.

Depending on the current construction stage when the exception is


raised, the automatic cleanup process will start at different destruction
stages to perform cleanup of any memory or other allocated resources.

If memory allocation throws the exception, no further cleanup is needed.


If the constructor throws the exception - cleanup will start with the
destructor
If AfterConstruction throws the exception - cleanup will start with
BeforeDestruction

7.2.2 The destruction process must not throw exceptions

The BeforeDestruction method - and destructors themselves - must never


ever throw exceptions. Doing that would break up the destruction chain,
introduce memory leaks and leave the application open to unpredictable
behavior. This rule is quite often ignored or forgotten.

7.2.3 Destructors and other related methods must be capable of


cleaning up partially constructed instances

The cleanup process ensures that properly handled exceptions during


object construction don't have any further negative impact on application
execution. In other words, raising an exception in the constructor is
considered a recoverable thing in Delphi, and applications can continue
running smoothly. Any methods that can be called during object cleanup
must be capable of handling partially constructed instances without
throwing unhandled exceptions of any kind.

Of course, there is a difference between running into an EOutOfMemory


exception, or an EFOpenError . Hitting EOutOfMemory is usually a showstopper for
the application, this is the kind of exception from which we may not be
able to recover smoothly, while EFOpenError is one we can easily recover
from as long as the file we are trying to open is not critical for the
application's functionality.
7.3 Creating and destroying an object instance
Constructors are a special kind of method. There is more to them than
meets the eye. If you ignore the constructor keyword, they are declared as
regular procedural methods, but they behave like class methods and
actually return a value - the newly created object instance - or, if called
from inside another constructor or on an instance, they act like regular
methods. The trick is in an additional hidden boolean parameter passed
to the constructor that toggles different behavior - DoAlloc .

The implicit first parameter is Self , just like with any other method, but Self
can represent a reference to either a class or an object instance. If a
constructor is called through a class or metaclass, Self will contain a
reference to that class, and the value of DoAlloc will be set to True indicating
that we are dealing with a class and that we want to create a new object
instance. That will result in calling the NewInstance class method which will
actually construct and return it. On the other hand, when a constructor is
called on an object instance, DoAlloc will be False , indicating that we are
already dealing with an object instance passed through the Self
parameter.

In other words, a constructor declaration observed as a function would


look like
function(Self: Pointer; [actual constructor params], DoAlloc: Boolean): Pointer

So, to create an object instance, you have to call a constructor on the


desired class and not on the variable itself. For example, we can create a
TStringList instance and store its reference in sl with the following code:

procedure Hello;
var
sl: TStringList;
begin
sl := TStringList.Create;
sl.Add('Hello World');
end;

And this is just about all we have to do under the ARC compiler, since the
destruction of object instances is automatically managed by the compiler.
Under manual memory management, the above code would create a
memory leak since we have to take care of destroying our TStringList
instance when we are done using it. Destruction is triggered by calling
the Free method on the object reference we want to destroy.
...
sl.Free;
end;

In order to ensure proper memory cleanup in the event that an exception


is raised while we use the object instance, we also have to protect the
destruction code by wrapping it in a try...finally block that will always be
executed. Again, this kind of code is not necessary under ARC.
procedure HelloFree;
var
sl: TStringList;
begin
sl := TStringList.Create;
try
sl.Add('Hello World');
finally
sl.Free;
end;
end;

It is important to leave the constructor call outside the try...finally block,


for several reasons. First, if an exception is raised during construction,
the compiler will automatically call the appropriate cleanup methods; and
second and more important, if an exception is raised during construction,
nothing will be assigned to the sl variable, so we cannot call Free on it
even if we want to.

"No problem," you may say. Calling Free on a nil reference is a safe thing
to do, so you are just making a redundant call and nothing else.

Wrong!

In this case, sl is a local non-managed variable (under the classic


compiler) and it will not be automatically initialized to nil . So if an
exception is thrown during construction, you will end up calling Free on a
wild reference. It may go right, or it may go wrong.
However, if we initialize sl before using it, we would solve the wild
reference issue and the following code would function properly even if an
exception is raised during construction.
procedure HelloFreeInit;
var
sl: TStringList;
begin
sl := nil;
try
sl := TStringList.Create;
sl.Add('Hello World');
finally
sl.Free;
end;
end;

While functionally this kind of code would work perfectly, it is not exactly a
text-book example of code you should be using. When it comes to
wrapping temporary object creation in try...finally blocks, the previous
HelloFree example should be used as proper template. But, there is a
reason why I am showing HelloFreeInit here, beyond it merely being an
example of bad code.

Sometimes we have to create more than one temporary object instance.


The properly protected construction and destruction of such object
instances would look like the following:
procedure DoubleTrouble;
var
Foo: TFoo;
Bar: TBar;
begin
Foo := TFoo.Create;
try
Bar := TBar.Create;
try
// do something with Foo and Bar
finally
Bar.Free;
end;
finally
Foo.Free;
end;
end;
Nested try...finally blocks are not exactly making the whole thing very
readable. Imagine having more than two instances:
procedure FooBarParty;
var
Foo: TFoo;
Bar: TBar;
FooBar: TFooBar;
begin
Foo := TFoo.Create;
try
Bar := TBar.Create;
try
FooBar := TFooBar.Create;
try
// do some real Foo Bar
finally
FooBar.Free;
end;
finally
Bar.Free;
end;
finally
Foo.Free;
end;
end;

Now, that escalated quickly. Even if you separated the creation logic from
the actual work to be done, and moved that part into another procedure,
you would still have quite a mess on your hands.

If we apply the logic from the HelloFreeInit example, we can actually merge
multiple try...finally blocks into one, significantly improving code
readability.
procedure GreatParty;
var
Foo: TFoo;
Bar: TBar;
FooBar: TFooBar;
begin
Bar := nil;
FooBar := nil;
Foo := TFoo.Create;
try
Bar := TBar.Create;
FooBar := TFooBar.Create;
// do some real Foo Bar
finally
Foo.Free;
Bar.Free;
FooBar.Free;
end;
end;

There is one thing worth mentioning here. If Foo.Free or Bar.Free raise an


exception, the lines that follow will not be executed, resulting in leaking
either the FooBar and Bar instances, or just FooBar . However, one of the
absolute rules in Delphi is that destructors should never, ever raise
exceptions. So merging multiple try...finally blocks into one is perfectly
acceptable, unless some of the classes involved have a faulty destructor.
In that case, the faulty destructor represents the actual piece of code that
needs to be corrected. In rare situations, when fixing faulty destructors is
beyond your reach, you must use separate try...finally blocks, or make
sure that you call Free on the faulty destructor last (if there is only single
faulty destructor). And don't forget sending a bug report for the faulty
destructor to whoever authored that class.

Since ARC automatically handles object destruction regardless of any


exceptions, our GreatParty code written solely for ARC would be even
simpler. This is a perfect example of how automatic memory
management can help us write cleaner code.
procedure GreatARCParty;
var
Foo: TFoo;
Bar: TBar;
FooBar: TFooBar;
begin
Foo := TFoo.Create;
Bar := TBar.Create;
FooBar := TFooBar.Create;
// do some real Foo Bar
end;

7.4 Overview of object lifecycle methods


Delphi provides a rather flexible design for construction of object
instances. Besides the methods commonly used for customizing the
initialization and finalization process, specifically constructors,
destructors, AfterConstruction and BeforeDestruction , it also allows advanced
customization through overriding the memory allocation methods,
providing highly specialized handling on a per-class basis. There are also
some differences in the provided methods and fields between the classic
and ARC compilers, due to the reference counting mechanism.

7.4.1 TObject lifecycle method declarations in the classic compiler


TObject = class
public
constructor Create;
destructor Destroy; virtual;

procedure Free;
procedure DisposeOf; inline;

procedure AfterConstruction; virtual;


procedure BeforeDestruction; virtual;

class function NewInstance: TObject; virtual;


class function InitInstance(Instance: Pointer): TObject;

procedure FreeInstance; virtual;
procedure CleanupInstance;
protected
function GetDisposed: Boolean; inline;
procedure CheckDisposed; inline;
property Disposed: Boolean read GetDisposed;
end;

7.4.2 TObject lifecycle method declarations in the ARC compiler


TObject = class
public
constructor Create;
protected
destructor Destroy; virtual;
public
procedure Free;
procedure DisposeOf;

procedure AfterConstruction; virtual;


procedure BeforeDestruction; virtual;

class function NewInstance: TObject unsafe; virtual;
class function InitInstance(Instance: Pointer): TObject unsafe;

procedure FreeInstance; virtual;


procedure CleanupInstance;
private const
objDestroyingFlag = Integer($80000000);
objDisposedFlag = Integer($40000000);
protected
[Volatile] FRefCount: Integer;
class procedure __MarkDestroying(const Obj); static; inline;
class function __SetDisposed(const Obj): Boolean; static; inline;
function GetDisposed: Boolean; inline;
procedure CheckDisposed;
public
function __ObjAddRef: Integer; virtual;
function __ObjRelease: Integer; virtual;
property RefCount: Integer read FRefCount;
property Disposed: Boolean read GetDisposed;
end;

7.4.3 Constructors

Constructors are marked with the reserved word constructor


The root TObject class provides a default public constructor declared as
constructor Create;
Classes can declare multiple constructors (or none)

7.4.4 Destructors

Destructors are marked with the reserved word destructor


The root TObject class provides a default public destructor declared as
destructor Destroy; virtual;
Under the ARC compiler, Destroy has different visibility and is marked as
protected.
While compiler allows declaring multiple destructors (or none), classes that
implements custom destructor should always override the default destructor
Destroy and call inherited inside to provide proper object deinitialization.

7.4.5 Free, DisposeOf

Specialized methods for invoking the default destructor on an object


reference, which can be called on a nil reference. Their functionality and
use cases are covered in detail in Part 3 - Releasing object instances and
Part 4 - DisposeOf

7.4.6 AfterConstruction

Called automatically after the last constructor in the chain is called.


Override to add any initialization code that requires a fully created object
instance.

7.4.7 BeforeDestruction

Called automatically before the first destructor in the chain is called.


Override to add any finalization code that requires a fully created object
instance.
This is the counterpart of the AfterConstruction method, any initialization
code there that needs appropriate finalization should go into
BeforeDestruction

7.4.8 Memory management methods - NewInstance, InitInstance,


FreeInstance, CleanupInstance

These methods handle the actual memory allocation and deallocation


process, as well as primary initialization of allocated memory and
cleanup. Overriding NewInstance and FreeInstance allows you to customize
memory allocation and bypass the default memory manager on a per-
class basis.

7.4.9 ARC related methods and fields

To support the reference counting mechanism, ARC has additional


methods and fields. Besides the RefCount property that holds the current
reference count of a particular object instance, and the reference
counting methods __ObjAddRef and __ObjRelease , there are a few methods and
fields used in direct destruction of the object instance through the DisposeOf
method. Calling DisposeOf will set the Disposed property to True to indicate that
the instance has entered a disposed, zombie state and can no longer be
used. You can find more in Part 4 and the ARC-related chapters.
Even though DisposeOf and the Disposed property are also accessible in
classic compilers, their purpose there is merely providing cross-platform
code compatibility. DisposeOf translates to Free , and Disposed always returns
False in classic compilers.
8 Lifecycle Coding Patterns
8.1 Constructors and destructors
The most commonly customized lifecycle methods are constructors and
destructors. This is where custom initialization and finalization of object
instances is done, and where the construction and destruction,
respectively, of owned inner fields and other resources should take place.
An exception to this rule is lazy initialized fields that will be constructed
on an as-needed basis, at a later time.

Constructors and destructors support only the default register calling


convention. Trying to use any other - pascal , cdecl , stdcall , safecall - will
result in a compile-time error:

E2236 Constructors and destructors must have REGISTER calling


convention

Delphi classes can implement any number of constructors, virtual or


static. Constructors are conventionally named Create and overloaded if
different parameters are needed. While you can use any other name,
following this convention is preferred unless you have a very particular
reason not to. If, for some reason, you need two different constructors
with the same set of parameters, you will have to give them different
names, since Delphi will not be able to overload methods with the same
parameter list.

Constructors follow the same overriding rules as all methods, but the fact
that the TObject class has a default public constructor Create will somewhat
limit your ability to achieve certain things.

While the compiler allows declaring multiple destructors (or none),


classes that implement a custom destructor should always override the
default destructor Destroy and call inherited inside to provide proper object
deinitialization. Using additional destructors, while technically possible, is
not recommended.

Let's start with constructors and the fact that TObject has a default static
parameterless constructor. Declaring any public constructor, with or
without parameters, will hide that default parameterless constructor. That
prevents you from calling the wrong constructor if your class needs to be
constructed with certain parameters. If you don't declare any new
constructors inside the class, its ancestors' constructors will be
automatically available. What you cannot do is completely hide all
constructors.
type
TFoo = class(TObject)
strict private
constructor Create;
public
end;

constructor TFoo.Create;
begin
inherited;
Writeln('TFoo constructor');
end;

var
Foo: TFoo;
begin
Foo := TFoo.Create;
Foo.Free;
end.

Running the above example will output nothing, because the constructor
declared in TFoo is hidden and cannot be accessed outside the class. The
default constructor declared in the TObject class is accessible and it will be
called instead. If the TFoo constructor is moved from the strict private
section to the public section, you will get TFoo constructor as output. There is
no way you can prevent instantiation of an object instance in publicly
available classes by hiding its constructors.

When declaring multiple constructors with the same name and different
parameters, you will have to use the overload directive. Overloading
constructors will also include overloaded constructors from the ancestors.
However, if you don't use the overload directive in the descendant class,
you will hide ancestor constructors.

Consider the following code: TFoo has two overloaded constructors, but
TBar does not and it will hide them. Trying to call TBar.Create(1) and invoking
a TFoo constructor overload will fail to compile with an error:

E2034 Too many actual parameters

type
TFoo = class(TObject)
public
constructor Create; overload;
constructor Create(AValue: integer); overload;
end;

TBar = class(TFoo)
public
constructor Create;
end;

constructor TFoo.Create;
begin
inherited;
Writeln('TFoo');
end;

constructor TFoo.Create(AValue: integer);


begin
inherited Create;
Writeln('TFoo Integer');
end;

constructor TBar.Create;
begin
inherited;
Writeln('TBar');
end;

var
Bar: TBar;

begin
Bar := TBar.Create;
Bar.Free;
end.

TFoo
TBar

Adding the overload directive to the TBar constructor will remove the
compiler error, and we will be able to construct TBar by directly invoking
the TFoo constructor. However, if we directly invoke the TFoo constructor,
the TBar constructor call will be skipped, and if the TBar constructor
contained any critical code, we would need to add the appropriate
constructor overload to the TBar class.
TBar = class(TFoo)
public
constructor Create; overload;
end;

...

Bar := TBar.Create(1);

TFoo Integer

As previously said, overriding a destructor in any particular class is


simple. If you don't have any special code to add into the destructor, you
don't have to override it with an empty implementation. Override the
destructor only when you have some code to add there.
TFoo = class(TBar)
public
...
destructor Destroy; override;
end;

destructor TFoo.Destroy;
begin
// your finalization code here
...
// call inherited Destroy
inherited;
end;

The following is the minimal and the most common initialization and
finalization pattern of an owned inner object instance:
type
TBar = class(TObject)
end;

TFoo = class(TObject)
private
FBar: TBar;
public
constructor Create;
destructor Destroy; override;
end;

constructor TFoo.Create;
begin
inherited;
FBar := TBar.Create;
end;

destructor TFoo.Destroy;
begin
FBar.Free;
inherited;
end;

Constructing dependent objects in the constructor and destroying them in


the destructor reduces the likelihood of memory issues, since it is
guaranteed that those object instances will be available throughout the
owning object's lifetime. Possible memory issues will be confined to the
construction and destruction processes, where they can be solved more
easily than during an instance's lifetime and interactions with other code.

An exception to the above rule is lazy initialized fields. Since lazy


patterns come in many forms and have many specialized variations, they
will be covered later in the book. For now, it is sufficient to say that
access to lazy fields must be protected and they should never be
accessed directly. Unfortunately, that kind of practice is rarely followed -
most often lazy fields are protected from outside consumers, but their
access is not commonly protected from within the class. In more complex
classes that can pose a real problem.

In the case of non-owned fields that represent optional collaborators,


there are three things to keep in mind:

Never free a field that you don't own


Always check a non-owned field for nil before using it
Make sure that a designated non-owned instance is either always valid
during the lifetime of the instance that holds its reference, or that there is a
mechanism in place to reset such a field back to nil if it is destroyed

8.2 AfterConstruction and BeforeDestruction


Any initialization that should take place after the object instance is fully
constructed should be done by overriding the AfterConstruction method. Any
related finalization should be implemented in its counterpart, the
BeforeDestruction method.

Those methods are underutilized, almost to the point that makes you
wonder if developers are even aware of their existence and purpose.
Sometimes, we are just too lazy or keeping it simple. Overriding and
customizing additional methods is not always worth the trouble,
especially in short code, but in complex classes with long inheritance
chains, that is less of a concern and many issues related to initialization
or finalization order, especially in destructor chains, could easily be
solved by moving the appropriate code from constructors and destructors
into the AfterConstruction and BeforeDestruction methods. Any code that needs
checking whether the object or its parts may already have been
constructed or destroyed is a good candidate for moving. Of course, that
is not always possible due to other possible requirements or interactions
with existing code, but it is an option well worth exploring.

8.3 Adding the override directive to Destroy,


AfterConstruction and BeforeDestruction is
mandatory
This may seem like something that does not deserve so much attention.
But, my experience tells me otherwise. Everyone has forgotten to write
override at one time or another. An experienced developer will probably
recognize his mistake soon enough. A less experienced one might not.
They might not even be aware that this is something they absolutely must
do.
Adding the override directive to Destroy , AfterConstruction and BeforeDestruction
methods is mandatory, otherwise your implementation will not get
called.

Fortunately, the compiler will issue a warning if we ever forget to do so.


That is also a good reason to look for and fix warnings emitted by the
compiler.

W1010 Method 'Destroy' hides virtual method of base type 'TObject'

W1010 Method 'AfterConstruction' hides virtual method of base type


'TObject'

W1010 Method 'BeforeDestruction' hides virtual method of base type


'TObject'

This is the most basic example of how to properly declare and customize
the above mentioned methods:
type
TFoo = class(TObject)
public
destructor Destroy; override;
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
end;

destructor TFoo.Destroy;
begin
Writeln('Destroy');
inherited;
end;

procedure TFoo.AfterConstruction;
begin
inherited;
Writeln('AfterConstruction');
end;

procedure TFoo.BeforeDestruction;
begin
inherited;
Writeln('BeforeDestruction');
end;

var
Foo: TFoo;

begin
Foo := TFoo.Create;
Foo.Free;
end.

Executing the above code will result in the following output:


AfterConstruction
BeforeDestruction
Destroy

However, if you remove any of the override directives, that particular


method will not be executed.

8.4 Always call inherited in constructors,


destructors, AfterConstruction and
BeforeDestruction

It is important to call inherited when overriding any of those methods to


make sure all the methods in its inheritance chain are executed to
prevent potential bugs and memory leaks.

Even if the ancestor's method implementation is empty, it is prudent to


call inherited anyway, because future changes might introduce code into
such an empty implementation and break our code. This also prevents
refactoring errors when you change the ancestor of your class, as the
new ancestor might also have a different implementation.

The following example has two classes, TFoo and TFooBar , that inherit from
TFoo . All methods are properly declared and overridden, and all
implementations correctly call their inherited methods.
type
TFoo = class(TObject)
public
constructor Create;
destructor Destroy; override;
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
end;

TFooBar = class(TFoo)
public
constructor Create;
destructor Destroy; override;
procedure AfterConstruction; override;
procedure BeforeDestruction; override;

procedure Run;
end;

constructor TFoo.Create;
begin
inherited;
Writeln('TFoo Create');
end;

destructor TFoo.Destroy;
begin
Writeln('TFoo Destroy');
inherited;
end;

procedure TFoo.AfterConstruction;
begin
inherited;
Writeln('TFoo AfterConstruction');
end;

procedure TFoo.BeforeDestruction;
begin
inherited;
Writeln('TFoo BeforeDestruction');
end;

constructor TFooBar.Create;
begin
inherited;
Writeln('TFooBar Create');
end;

destructor TFooBar.Destroy;
begin
Writeln('TFooBar Destroy');
inherited;
end;

procedure TFooBar.AfterConstruction;
begin
inherited;
Writeln('TFooBar AfterConstruction');
end;

procedure TFooBar.BeforeDestruction;
begin
inherited;
Writeln('TFooBar BeforeDestruction');
end;

procedure TFooBar.Run;
begin
Writeln('Running');
end;

var
FooBar: TFooBar;

begin
FooBar := TFooBar.Create;
try
FooBar.Run;
finally
FooBar.Free;
end;
end.

Running the example will produce the following output:


TFoo Create
TFooBar Create
TFoo AfterConstruction
TFooBar AfterConstruction
Running
TFoo DeforeDestruction
TFooBar BeforeDestruction
TFooBar Destroy
TFoo Destroy
The first thing we notice is that even though our constructors are static,
and the TFooBar constructor basically hides the TFoo constructor from the
outside world, the TFoo constructor is still accessible from within the TFooBar
class using the inherited keyword, ensuring full execution of the
construction chain.

The order of execution follows the order in which we called inherited .


Moving inherited to another place inside the method will change the order
of execution. For instance, if we change the TFooBar constructor:
constructor TFooBar.Create;
begin
Writeln('TFooBar Create');
inherited;
end;

We will get the following output:


TFooBar Create
TFoo Create
TFoo AfterConstruction
...

If we remove the call to inherited inside the TFooBar constructor, we can see
that the inherited constructor will never be executed. The same goes for
any other method. Without calling inherited , we break the inherited
execution chain.
constructor TFooBar.Create;
begin
Writeln('TFooBar Create');
end;

Results in:
TFooBar Create
TFoo AfterConstruction
...

Usually you can place a call to inherited at any point in the method call, as
long as the logic of your code does not depend on any particular code in
the inherited chain being executed or not. Since finalization logic is
usually the reverse of the initialization logic, the most common ordering is
calling inherited first in constructors, and last in destructors.

The following example demonstrates code where the order of execution


is important. For the code inside the TFooBar constructor, it is important that
the TFoo constructor runs first and constructs the Lines instance.
uses
System.SysUtils,
System.Classes;

type
TFoo = class(TObject)
public
Lines: TStringList;
constructor Create;
destructor Destroy; override;
end;

TFooBar = class(TFoo)
public
Copy: TStringList;
constructor Create;
destructor Destroy; override;
procedure AfterConstruction; override;
end;

constructor TFoo.Create;
begin
inherited;
Lines := TStringList.Create;
Lines.Add('Line');
end;

destructor TFoo.Destroy;
begin
Lines.Free;
inherited;
end;

constructor TFooBar.Create;
begin
inherited;
Copy := TStringList.Create;
Copy.Text := Lines.Text;
end;

destructor TFooBar.Destroy;
begin
Copy.Free;
inherited;
end;

procedure TFooBar.AfterConstruction;
begin
inherited;
Writeln('Lines created: ', Assigned(Lines));
end;

var
FooBar: TFooBar;

begin
try
FooBar := TFooBar.Create;
try
Writeln(FooBar.Copy.Text);
finally
FooBar.Free;
end;
except
on E: Exception do Writeln(E.ClassName, ': ', E.Message);
end;
end.

Running the code will produce:


Lines created: TRUE
Line

Changing the position of the inherited call in the TFooBar constructor will
produce an access violation exception.
constructor TFooBar.Create;
begin
Copy := TStringList.Create;
Copy.Text := Lines.Text; // Lines instance is not constructed at this point
inherited;
end;

If we remove the line where we are trying to access the unconstructed


Lines instance, and replace it with a test:

constructor TFooBar.Create;
begin
Copy := TStringList.Create;
Writeln('TFooBar constructor Lines created: ', Assigned(Lines));
inherited;
end;

TFooBar constructor Lines created: FALSE


Lines created: TRUE

The program will run without exceptions and will show that the Lines
instance is successfully created when we reached the AfterConstruction
method, but it is still not initialized inside the TFooBar constructor before we
called inherited .

On the other hand, changing the location of inherited in the destructor will
not have any negative impact on our test code, because in this case the
destructors don't have any dependencies that would require a different
order of execution.

This is also a good place to repeat that destructors should never throw
exceptions, and must be able to deal with half-constructed object
instances. So if the order of destructor execution matters, make sure that
you don't forget to protect against possible exceptions that might be
raised if the constructors didn't have the chance to complete their work.

Usually, the order of execution in any of the above methods should not
matter, except in the case of construction and destruction of dependent
objects. For instance, if some previously constructed object must be
passed as a parameter to the constructor of some other object field. The
destruction order of such connected instances may also be strictly
defined.

If the order of execution matters in other cases, that might indicate that
the order-dependent code might not belong in constructors or
destructors. For instance, the code in the TFooBar constructor that initializes
Copy from the Lines instance could be moved into AfterConstruction , and we
could safely place calls to inherited anywhere we like.
constructor TFooBar.Create;
begin
Copy := TStringList.Create;
inherited;
end;
procedure TFooBar.AfterConstruction;
begin
inherited;
Writeln('Lines created: ', Assigned(Lines));
Copy.Text := Lines.Text;
end;

There is no strict rule that requires moving such dependent code from
constructors and destructors to AfterConstruction and BeforeDestruction . In
simple code, like the previous example, it really does not matter where
the Copy.Text initialization is, as long as the code is properly written. It is up
to the developer to decide when moving particular code from constructors
or destructors is beneficial.

On rare occasions, you might need to omit an inherited call in order to


avoid or change particular behavior. If you really, really must do that,
make sure that you implement all the necessary code for proper
initialization and finalization from the inherited methods you will not call.
Be aware, that any code upgrades in your ancestor path might break
your code, and that you have to revisit this kind of code each and every
time an upgrade occurs. This is one of those situations where you must
do what you must do, but you should really think twice before you do it.

Also, if necessary, you can skip calls to immediate ancestor(s) and call
any particular ancestor constructor, prefixing the constructor call with the
ancestor's type. If you do that, make sure that you implement any vital
functionality from the constructors you have skipped.
type
TFoo = class(TObject)
public
constructor Create;
end;

TBar = class(TFoo)
public
constructor Create;
end;

TFooBar = class(TBar)
public
constructor Create;
end;
constructor TFoo.Create;
begin
inherited;
Writeln('TFoo constructor');
end;

constructor TBar.Create;
begin
inherited;
Writeln('TBar constructor');
end;

constructor TFooBar.Create;
begin
// skip call to TBar constructor and call TFoo constructor
TFoo.Create;
Writeln('TFooBar constructor');
end;

var
FooBar: TFooBar;
begin
FooBar := TFooBar.Create;
FooBar.Free;
end.

Running the above example will output:


TFoo constructor
TFooBar constructor

8.5 Class constructors and destructors


Class constructors and destructors are marked with the keyword class ,
just like any other class members. Class constructors will be executed
before the initialization section of the unit where the class is declared,
and class destructors will execute after unit finalization. They will be
executed automatically for all linked classes and you cannot call them
directly. The class constructors and destructors of generic classes will be
executed separately for each type specialization.

Class constructors and destructors cannot be marked as static , virtual ,


dynamic or message , and they must be parameterless, but you can give them
any name you like. Class constructors and destructors are independent
of each other, you can use them in all possible combinations. You don't
have to provide a constructor if you have declared a destructor and vice
versa, you can have both, or you can have neither. What you cannot
have, are multiple class constructors or destructors in a single class.

Class constructors and destructors are called automatically for any used -
touched - class and that implies automatic execution of all class
constructors and destructors in its ancestor chain - you don't have to call
inherited in your class constructor or destructor.

Since there is no way to tell which class will be used and which won't in
dynamically loaded packages via the LoadPackage function, all contained
classes' constructors and destructors will be executed.

Order of execution of class constructors and destructors:

Class constructors and destructors will be executed in sequence with their


respective unit initialization and finalization sections
Constructors will run immediately before the unit's initialization section
Destructors will run immediately after the finalization section
Destructors will run in reverse order of the constructors
In general, constructors will run by order of declaration
Descendant class constructors will run after their ancestor's constructor if
they are declared within the same unit, but cyclic references might
influence ordering
In the event of a cyclic dependency, it is possible that a class constructor
runs before the class constructor of a class referenced in that constructor
An ancestor class' constructor from an external unit added in the interface
uses section will always execute before its descendant constructor
Unit cycles can also influence the order of execution

Beyond the simplest of cases, you should avoid complex dependencies


in class constructors and destructors as this kind of code can be very
fragile. Just the same as the code in initialization and finalization
sections, that require a specific ordering that can easily be broken by
changing the order of added units in the uses list.

There are two major differences between using class constructors and
destructors compared to the commonly-used initialization and finalization
sections.

First, class constructors and destructors provide better encapsulation and


granularity. Any code written in the initialization or finalization sections of
the unit will always be executed if the application has that particular unit
somewhere in its uses section. Besides better encapsulation, moving code
into the class has another advantage, because it will be linked into the
final application, and executed only if you explicitly use that particular
class somewhere in other code.

Second, class destructors run after the unit finalization section, and at
that point all managed variables - including strings - will already be
cleared at that point so forget about doing anything substantial there,
besides merely releasing allocated class fields.
type
TBase = class
public
class var Value: Integer;
class var Text: string;
class constructor ClassCreate;
class destructor ClassDestroy;
end;

class constructor TBase.ClassCreate;


begin
Value := 5;
Text := 'Full';
end;

class destructor TBase.ClassDestroy;


begin
Writeln(Value);
Writeln(Text);
end;

begin
Writeln(TBase.Value);
Writeln(TBase.Text);
end.

If you put a breakpoint into the above class destructor, you will see that
the integer field Value contains the expected value of 5, but the Text field
will be empty.
8.6 Object construction through metaclasses
Class reference types or metaclasses are types used for referencing
classes and declared as class of type where type is an actual class.
Besides the class used in its declaration, metaclass references can also
hold any of that class' descendant classes.

Example declarations of some commonly used metaclasses provided by


Delphi RTL:
type
TClass = class of TObject;

TInterfacedClass = class of TInterfacedObject;


TPersistentClass = class of TPersistent;
TCollectionItemClass = class of TCollectionItem;
TComponentClass = class of TComponent;

Just like TObject is a root type that can hold any object instance, TClass is a
root metaclass type that can hold any class.

Metaclasses are most commonly used in object factories for constructing


object instances belonging to a wider range of classes, specified by some
metaclass, and not just a particular hardcoded one.

Common examples are TCollection , in which stored items are created with
the help of the TCollectionItemClass metaclass, and the component streaming
system, in which components are created during the streaming process
using TComponentClass .

It is important to remember that when using a particular metaclass, the


type used in the metaclass' declaration will be used as the lowest
common denominator for any actual class stored. When accessing
methods, including constructors, descendant-specific method
implementations will be invoked only if they are overridden virtual or
dynamic methods.

Let's take a look at the following classes. Note that none of the
constructors are virtual.
type
TFoo = class(TObject)
public
Text: string;
constructor Create;
end;

TBar = class(TFoo)
public
constructor Create;
end;

TFooClass = class of TFoo;

constructor TFoo.Create;
begin
inherited;
Text := Text + 'Foo';
end;

constructor TBar.Create;
begin
inherited;
Text := Text + 'Bar';
end;

Now let's construct some objects in different ways. In all of the examples
that follow, the class of the constructed object instance will be the correct,
expected one, regardless of whether we are hard-coding the class or
using a metaclass.

Also, instance construction code is executed before assignment code.


The type of the variable to which we assign our newly created object
instance will have absolutely no effect on the construction process itself.

Following example creates a TFoo object instance and stores it in a


variable of the TObject type. Since the constructor is called on the
hardcoded TFoo class, the constructor declared in TFoo is invoked and the
object instance is properly constructed.
var
Obj: TObject;

Obj := TFoo.Create;
Writeln(Obj.ClassName, ', ', TFoo(Obj).Text);

=> TFoo, Foo
If we use TFoo as a variable type instead of TObject , there will be no
changes in behavior. The only difference is that we don't have to typecast
Obj to TFoo in order to access the Text field.

var
Obj: TFoo;

Obj := TFoo.Create;
Writeln(Obj.ClassName, ', ', Obj.Text);

=> TFoo, Foo

Similarly, if we explicitly use the TBar class for object construction, our
object instance will be properly created, regardless of the assignment's
variable type.

Constructors will be invoked in the following order: The TBar constructor


will be invoked first, because we explicitly called Create on the TBar class.
That constructor first calls inherited , which invokes the TFoo constructor and
adds Foo to the Text field. When the TBar constructor resumes its
construction process, it will add Bar to Text , resulting in FooBar .
var
Obj: TFoo;

Obj := TBar.Create;
Writeln(Obj.ClassName, ', ', Obj.Text);

=> TBar, FooBar

However, using metaclasses to construct object instances takes a slightly


different execution path.

If you think that the following example will yield the same result as the
first one, you might get a nasty surprise. The created object instance will
be of the TFoo class, but the TFoo constructor will not be invoked???
var
MetaClass: TClass;
Obj: TObject;

MetaClass := TFoo;
Obj := MetaClass.Create;
Writeln(Obj.ClassName, ', ', TFoo(Obj).Text);

=> TFoo,

So what exactly happened here?

MetaClass is of the TClass type, declared as class of TObject . Calling Create on a


MetaClass variable will invoke the Create constructor in the TObject class - the
default constructor. Since this constructor is static, it does not know that
there is another Create constructor in the TFoo class.

Still does not make any sense, does it? Why is the object constructed
with the right type, while the wrong constructor gets called?
MetaClass.Create

The above piece of code is crucial. How does that piece of code work?

As we already know, the first parameter passed to constructor calls like


TFoo.Create or MetaClass.Create will be a reference to a class - in this case, TFoo
or a reference to the class stored in MetaClass , which is also TFoo in this
case. So far, so good. In both cases, the passed class will be TFoo , as
expected.

At compile-time, the compiler can resolve the hardcoded TFoo.Create as a


call to the constructor Create declared in TFoo , but when it comes to
MetaClass.Create , the only firm ground it has is the TObject class, used as the
base type in the TClass declaration. So it will translate MetaClass.Create to
TObject.Create , and as we already know, we cannot go down the
descendant chain with a static method.

If we want to call the TFoo class' constructor, we have to use a TFoo


metaclass, in which case the proper constructor will be used.
var
FooClass: TFooClass;
Obj: TObject;

FooClass := TFoo;
Obj := FooClass.Create;

Writeln(Obj.ClassName, ', ', TFoo(Obj).Text);



=> TFoo, Foo

However, if we want to create a TBar instance using the TFooClass


metaclass, we would bump into the same issue as with TClass , only this
time TFoo.Create will be invoked, since TFooClass has TFoo as its base type.
var
FooClass: TFooClass;
Obj: TObject;

FooClass := TBar;
Obj := FooClass.Create;

Writeln(Obj.ClassName, ', ', TBar(Obj).Text);



=> TBar, Foo

If we want to execute a proper chain of constructors, we have to change


our type declarations, and replace static constructors with virtual ones.
type
TFoo = class(TObject)
public
Text: string;
constructor Create; virtual;
end;

Creating a TBar instance with TFooClass should work now. Well, not really.
We have marked TFoo.Create as virtual, but TBar.Create is still a static
constructor. If we look at the compiler output we can notice the following
warning pointing to the Create declaration in TBar class:

W1010 Method 'Create' hides virtual method of base type 'TFoo'

This is a rather important compiler warning. The whole point of having


virtual methods is overriding them so we would have a proper chain of
execution. This warning is basically saying, you forgot to put in the
override directive. In rare situations when you really do want to hide some
virtual method or constructor, you can silence the compiler warning by
using the reintroduce directive instead - saying I know what I am doing
here, leave me alone.
In our case, we do want to use proper constructors so we will fix our code
accordingly.
type
TFoo = class(TObject)
public
Text: string;
constructor Create; virtual;
end;

TBar = class(TFoo)
public
constructor Create; override;
end;

var
FooClass: TFooClass;
Obj: TObject;

FooClass := TBar;
Obj := FooClass.Create;

Writeln(Obj.ClassName, ', ', TBar(Obj).Text);



=> TBar, FooBar

will again be translated to TFoo.Create , but since Create is now a


FooClass.Create
virtual constructor overridden in the TBar class, the compiler can now use
dynamic dispatch through the VMT table and call the appropriate one.

While the above examples used parameterless constructors, this is not a


requirement. We can call any constructor on a metaclass regardless of its
parameters.
type
TFoo = class(TObject)
public
Name: string;
Text: string;
constructor Create(const AName: string); virtual;
end;

TBar = class(TFoo)
public
constructor Create(const AName: string); override;
end;
TFooClass = class of TFoo;

constructor TFoo.Create(const AName: string);


begin
inherited Create;
Name := AName;
Text := Text + 'Foo';
end;

constructor TBar.Create(const AName: string);


begin
inherited;
Text := Text + 'Bar';
end;

var
FooClass: TFooClass;
Obj: TFoo;

FooClass := TBar;
Obj := FooClass.Create('Nothing');

Writeln(Obj.ClassName, ', ', Obj.Text, ', ', Obj.Name);



=> TBar, FooBar, Nothing

In order to successfully construct object instances through a metaclass


we have to make sure that the base class used in the metaclass
declaration has its constructor declared such that it can be used to
properly construct an instance of any descendant class. Such a
constructor can either be static, in which case none of the descendant
classes is allowed to hide it, or it must be virtual in which case
descendant classes must properly override it or use it as-is.

8.7 Object factories


Not all functions that return a newly created object instance qualify as an
object factory. Functions that return newly created object instances are
quite often inappropriately used in Delphi.

A common coding pattern in languages with automatic memory


management systems is creating an object inside some function and
returning it to the caller. Usually, such functions don't have names that
would indicate the construction of a new instance. Since all object
instances are automatically managed, functions can freely construct and
return a new object because ownership transfer is implicit.

In Delphi, such an approach is not suitable because the receiver of the


object also receives the responsibility for releasing an object that he may
not be aware of. Such coding patterns make manual memory
management harder as they go against the principle that the creator of
an object instance should release that object instance, or that the creator
of an object instance is the one that should pass object ownership to
someone else.

Ownership is something that should be given - passed on, not received -


returned.

For instance, if we need to implement a method that fills TStrings , the


following pattern, without any ownership transfer, using a procedure, is
more appropriate for Delphi:
procedure MovieTitles(Strings: TStrings);
begin
Strings.Add('Back to the Future');
Strings.Add('Ghostbusters');
Strings.Add('Spaceballs');
end;

var
Titles: TStrings;
begin
Titles := TStringList.Create;
try
MovieTitles(Titles);
// use Titles
....
finally
Titles.Free;
end;
end;

than a function with ownership transfer


function MovieTitles: TStrings;
begin
Result := TStringList.Create;
try
Result.Add('Back to the Future');
Result.Add('Ghostbusters');
Result.Add('Spaceballs');
except
Result.Free;
raise;
end;
end;

var
Titles: TStrings;
begin
Titles := MovieTitles;
try
// use Titles
...
finally
Titles.Free;
end;
end;

Besides, being a common coding pattern for Delphi, another advantage


of the first approach is simpler exception handling.

Object factories do have their place in Delphi, but they should be used
only when their primary purpose is to construct various objects, not when
a new object is merely a side effect. Naming such functions also plays a
significant role. A function that constructs the new object should have a
clear name that indicates its intent and purpose.

The Get... prefix in Delphi is commonly used for property getters and
should never be used for functions that transfer ownership.

Create..., New... , Make... , Build... are all appropriate, choosing the right one
also depends on the actual use case. Since Create is commonly used as a
constructor name, it should be avoided if there is the possibility of a
conflict or misinterpretation.

8.8 Object factory with metaclass


More complex construction logic with metaclasses is a good use case for
an object factory. For instance, imagine that you need to construct some
object where the lowest common class does not fully cover all the
possible specializations.
type
TFoo = class(TObject)
public
constructor Create; virtual;
end;

TFooClass = class of TFoo;

TFooBar = class(TFoo)
public
constructor Create; override;
end;

TBar = class(TObject)
public
constructor Create;
end;

TBarClass = class of TBar;

constructor TFoo.Create;
begin
inherited;
Writeln('TFoo Create');
end;

constructor TFooBar.Create;
begin
inherited;
Writeln('TFooBar Create');
end;

constructor TBar.Create;
begin
inherited;
Writeln('TBar Create');
end;

function ObjectFactory(ObjClass: TClass): TObject;


begin
if ObjClass.InheritsFrom(TFoo) then Result := TFooClass(ObjClass).Create
else
if ObjClass.InheritsFrom(TBar) then Result := TBarClass(ObjClass).Create
else Result := ObjClass.Create;
end;
var
Obj: TObject;
begin
Obj := ObjectFactory(TBar);
Obj.Free;

Obj := ObjectFactory(TFooBar);
Obj.Free;

Obj := ObjectFactory(TObject);
Obj.Free;
end.

The above ObjectFactory function is capable of creating multiple objects that


have different base metaclasses and require invoking a different base
constructor. Such situations can be found in serialization frameworks,
where you have to properly construct objects that don't necessarily have
a common root.

In this factory, we are using the InheritsFrom class function that determines
whether an object instance or - in our case, class - is or descends from
the class passed as its parameter. If we used the equality operator
instead, we would not be able to call the appropriate constructors for
descendant classes of TFoo and TBar .
function ObjectFactory(ObjClass: TClass): TObject;
begin
if ObjClass = TFoo then Result := TFooClass(ObjClass).Create
else
if ObjClass = TBar then Result := TBarClass(ObjClass).Create
else Result := ObjClass.Create;
end;

Obj := ObjectFactory(TFooBar);

Such a modified factory function would fail to call the appropriate


constructor for the TFooBar class because it is not equal to the TFoo class,
but inherits from it.

In the meantime, if you wonder what good is constructing such variety of


objects that don't share common functionality, and how to use them
without adding some other if...else ladder somewhere in the code, you
can add the following code to the above example and have some fun
playing with RTTI that helps us with invoking the correct overloaded
method based on parameter type.
uses
System.Rtti;

type
{$RTTI EXPLICIT METHODS([vcProtected, vcPublic])}
TWork = class
protected
procedure Use(Obj: TFoo); overload;
procedure Use(Obj: TBar); overload;
procedure Use(Obj: TObject); overload;
public
procedure Run(Instance: TObject);
end;

procedure TWork.Use(Obj: TFoo);


begin
Writeln('Using Foo ', Obj.ClassName);
end;

procedure TWork.Use(Obj: TBar);


begin
Writeln('Using Bar ', Obj.ClassName);
end;

procedure TWork.Use(Obj: TObject);


begin
Writeln('Using Object ', Obj.ClassName);
end;

procedure TWork.Run(Instance: TObject);


var
CurrentClass: TClass;
Params: TArray<TRttiParameter>;
ParamType: TRttiType;
Methods: TArray<TRttiMethod>;
Method: TRttiMethod;
begin
Methods := TRttiContext.Create.GetType(TWork).GetMethods;
CurrentClass := Instance.ClassType;

repeat
for Method in Methods do
begin
Params := Method.GetParameters;
if (Length(Params) = 1) and (Method.Name = 'Use') then
begin
ParamType := Params[0].ParamType;
if (ParamType.IsInstance) and
(ParamType.AsInstance.MetaclassType = CurrentClass) then
begin
Method.Invoke(Self, [Instance]);
exit;
end;
end;
end;
CurrentClass := CurrentClass.ClassParent;
until CurrentClass = nil;
end;

var
Obj: TObject;
Work: TWork;

begin
Work := TWork.Create;
try
Obj := ObjectFactory(TBar);
try
Work.Run(Obj);
finally
Obj.Free;
end;

Obj := ObjectFactory(TFooBar);
try
Work.Run(Obj);
finally
Obj.Free;
end;

Obj := ObjectFactory(TObject);
try
Work.Run(Obj);
finally
Obj.Free;
end;

finally
Work.Free;
end;
end.

TBar Create
Using Bar TBar
TFoo Create
TFooBar Create
Using Foo TFooBar
Using Object TObject

8.9 OnCreate, OnDestroy and similar patterns


While TObject provides basic constructor- AfterConstruction and
BeforeDestruction - destructor pairs for customization of the object
construction and deconstruction process, other classes are free to build
on top of those methods, add additional specialized methods, and define
different templates for customization.

One of the commonly used classes that does that is TCustomForm - it defines
two events, OnCreate and OnDestroy , that can be used from within the Form
Designer and which provide an additional place to construct and destroy
additional objects and perform additional initialization.

Since OnCreate and OnDestroy are invoked from AfterConstruction and


BeforeDestruction , we can also freely choose between overriding the
constructor and destructor to add customization code, or using the
OnCreate and OnDestroy events.

TCustomForm is a rather complex class, and the actual code behind OnCreate
and OnDestroy events, combined with the rest of the initialization and
finalization code, is not exactly straightforward. In order to present the
OnCreate and OnDestroy pattern as a simple template that can be extended
and applied for other purposes, I will cut the TCustomForm code down to the
point where it will no longer fully represent the exact initialization and
finalization behaviors of TCustomForm .
type
TCustomForm = class(TComponent)
private
FOnCreate: TNotifyEvent;
FOnDestroy: TNotifyEvent;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
published
property OnCreate: TNotifyEvent read FOnCreate write FOnCreate;
property OnDestroy: TNotifyEvent read FOnDestroy write FOnDestroy;
end;
constructor TCustomForm.Create(AOwner: TComponent);
begin
inherited;
end;

destructor TCustomForm.Destroy;
begin
inherited;
end;

procedure TCustomForm.AfterConstruction;
begin
inherited;
if Assigned(FOnCreate) then FOnCreate(Self);
end;

procedure TCustomForm.BeforeDestruction;
begin
inherited;
if Assigned(FOnDestroy) then FOnDestroy(Self);
end;

The OnCreate and OnDestroy events are published properties of the TCustomForm
class, and can be assigned through the Form Designer and then properly
connected through the form streaming mechanism, or they can be
attached through code. Since their primary purpose in TCustomForm is to
enable RAD simplicity for customizing forms, attaching them through
code makes very little sense in the context of forms.

This pattern was presented not only to show the behavior of forms, but
also to provide a template for applying a similar pattern in other cases.
For example, instead of having OnCreate and OnDestroy declared as events
that can be hooked through Form Designer, you can use anonymous
methods instead, to inject custom behavior into the class without the
need to declare a descendant class at all. Of course, such patterns do
not have to be related only to instance lifecycle, and can be used in other
places and for other purposes.

8.10 Deep copy - Assign method


Assignment of one object reference to another only copies the reference,
it does not create a copy of the object instance. The TPersistent class
provides a common method for creating deep copies of object instances
through the Assign method. Each descendant class must override that
method and provide a customized implementation. Without that
implementation, Assign will not properly perform its function.

If the Assign function does not recognize the Source object class and does
not know how to perform copying, you should call the default
TPersistent.Assign implementation, which will call the AssignTo method on the
Source in case the Source class supports copying for the destination object
class. If the AssignTo method does not know how to handle the incoming
object, it will raise an exception. Combined with AssignTo , Assign allows you
not only to create clones of the same type, but also to copy instances of
some other class.

This is the declaration of the TPersistent class as it pertains to the Assign


method:
TPersistent = class(TObject)
private
procedure AssignError(Source: TPersistent);
protected
procedure AssignTo(Dest: TPersistent); virtual;
public
procedure Assign(Source: TPersistent); virtual;
...
end;

var
Source, Dest: TSomePersistent;
...

// this will perform a shallow copy - reference only


Dest := Source;

// if implemented, this will perform a deep copy of the Source object


Dest.Assign(Source);

The following example demonstrates making deep copies of various


objects in different combinations. The assignment rules are not set in
stone. The possibilities and variations are unlimited. The exact
implementation depends on the particular needs and requirements of that
specific object hierarchy. It can range from following very strict rules and
allowing assignment only for particular classes, to a much more relaxed
system that will copy what it can.

We will start with the base TShape class and its descendants TCircle , TSquare
and TUnicorn .

To demonstrate creating deep copies between different classes that


share a common TPersistent descendant class as an ancestor, we will
allow TCircle to be copied to TSquare and vice versa, however TUnicorn can
only make deep copies of other TUnicorn object instances.

The Assign methods of TSquare and TCircle both know how to create copies
of each other, and AssignTo will not be invoked if we try copying one to the
other.

To show how AssignTo works, and how it can be used to extend deep
copying to new classes that don't have to be known by the Source class,
nor need to have a common ancestor besides TPersistent , we will use
TFloatShape and its descendant TFloatCircle . To implement AssignTo , one of the
classes has to know about the other. In this case, TShape does not have
any knowledge of TFloatShape or its descendant classes, but TFloatShape does
know about TShape and therefore can implement an AssignTo method that will
allow copying between those two classes and their respective class
hierarchies.

The TNotShape class will be another demonstration of classes that are not
mutually assignable. You cannot assign a TShape instance to a TNotShape ,
instance nor you can assign TNotShape to a TShape instance. Doing so will
raise an EConvertError exception.

Note the calls to inherited methods. They are crucial for implementing the
particular behavior.

To simplify the code, the construction and destruction of all object


instances in the main program block is not protected by try...finally
blocks.
unit Shapes;

interface

uses
System.Classes,
System.Types;

type
TShape = class(TPersistent)
protected
FName: string;
FPosX: Integer;
FPosY: Integer;
public
procedure Assign(Source: TPersistent); override;
procedure Write; virtual;
property Name: string read FName write FName;
property PosX: Integer read FPosX write FPosX;
property PosY: Integer read FPosY write FPosY;
end;

TCircle = class(TShape)
protected
FDiameter: Integer;
public
procedure Assign(Source: TPersistent); override;
procedure Write; override;
property Diameter: Integer read FDiameter write FDiameter;
end;

TSquare = class(TShape)
protected
FSize: Integer;
public
procedure Assign(Source: TPersistent); override;
procedure Write; override;
property Size: Integer read FSize write FSize;
end;

TUnicorn = class(TShape)
protected
FMagicSize: Integer;
public
procedure Assign(Source: TPersistent); override;
procedure Write; override;
property MagicSize: Integer read FMagicSize write FMagicSize;
end;

implementation

type
PPersistent = class(TPersistent);

procedure TShape.Assign(Source: TPersistent);


begin
// If we got here, the Source class is either a recognized,
// assignable class or an unsupported one.
// In the former case, we MUST NOT call inherited, as
// the execution path of Assign is fully completed.
// If the class is not recognized, we MUST call inherited
// and fall back to TPersistent's default behavior
// which will try to copy the object using AssignTo
if Source is TShape then
begin
FName := TShape(Source).Name;
FPosX := TShape(Source).PosX;
FPosY := TShape(Source).PosY;
end
else inherited;
end;

procedure TShape.Write;
begin
Writeln('Shape ', Name, ' X: ', PosX, ' Y: ', PosY);
end;

procedure TCircle.Assign(Source: TPersistent);


begin
if Source is TCircle then
begin
FDiameter := TCircle(Source).Diameter;
inherited;
end
else
if Source is TSquare then
begin
FDiameter := TSquare(Source).Size;
inherited;
end
else
// If we got here, we are dealing with an unrecognized class,
// which might be a TShape we don't explicitly support
// so we need to skip calling TShape.Assign and
// immediately fall back to an AssignTo call
if Source <> nil then PPersistent(Source).AssignTo(Self)
// If Source is nil, calling inherited will eventually
// trigger the appropriate EConvertError exception
else inherited;
end;

procedure TCircle.Write;
begin
Writeln('Circle ', Name, ' X: ', PosX, ' Y: ', PosY, ' D: ', Diameter);
end;
procedure TSquare.Assign(Source: TPersistent);
begin
if Source is TCircle then
begin
FSize := TCircle(Source).Diameter;
inherited;
end
else
if Source is TSquare then
begin
FSize := TSquare(Source).Size;
inherited;
end
else
if Source <> nil then PPersistent(Source).AssignTo(Self)
else inherited;
end;

procedure TSquare.Write;
begin
Writeln('Square ', Name, ' X: ', PosX, ' Y: ', PosY, ' Size: ', Size);
end;

procedure TUnicorn.Assign(Source: TPersistent);


begin
if Source is TUnicorn then
begin
FMagicSize := TUnicorn(Source).MagicSize;
inherited;
end
else
if Source <> nil then PPersistent(Source).AssignTo(Self)
else inherited;
end;

procedure TUnicorn.Write;
begin
Writeln('Unicorn ', Name, ' X: ', PosX, ' Y: ', PosY,
' Magic Size: ', MagicSize);
end;

end.

unit FloatShapes;

interface

uses
System.Classes,
System.Types;

type
TFloatShape = class(TPersistent)
protected
FName: string;
FX: Double;
FY: Double;
procedure AssignTo(Dest: TPersistent); override;
public
procedure Assign(Source: TPersistent); override;
procedure Write; virtual;
property Name: string read FName write FName;
property X: Double read FX write FX;
property Y: Double read FY write FY;
end;

TFloatCircle = class(TFloatShape)
protected
FDiameter: Double;
procedure AssignTo(Dest: TPersistent); override;
public
procedure Assign(Source: TPersistent); override;
procedure Write; override;
property Diameter: Double read FDiameter write FDiameter;
end;

implementation

uses
Shapes;

procedure TFloatShape.Assign(Source: TPersistent);


begin
if Source is TFloatShape then
begin
FName := TFloatShape(Source).Name;
FX := TFloatShape(Source).X;
FY := TFloatShape(Source).Y;
end
else
if Source is TShape then
begin
FName := TShape(Source).Name;
FX := TShape(Source).PosX;
FY := TShape(Source).PosY;
end
else inherited;
end;
procedure TFloatShape.AssignTo(Dest: TPersistent);
begin
if Dest is TShape then
begin
TShape(Dest).Name := Name;
TShape(Dest).PosX := Round(X);
TShape(Dest).PosY := Round(Y);
end
else inherited;
end;

procedure TFloatShape.Write;
begin
Writeln('Float Shape ', Name, ' X: ', X:0:2, ' Y: ', Y:0:2);
end;

procedure TFloatCircle.Assign(Source: TPersistent);


begin
if Source is TFloatCircle then
begin
FDiameter := TFloatCircle(Source).Diameter;
end
else
if Source is TCircle then
begin
FDiameter := TCircle(Source).Diameter;
end;
// The descendants of TFloatShape don't have a TFloatUnicorn class
// that cannot be assigned to any other TFloatShape descendant.
// We will just call inherited and copy common TFloatShape
// properties if we are dealing with any kind of TFloatShape
inherited;
end;

procedure TFloatCircle.AssignTo(Dest: TPersistent);


begin
if Dest is TCircle then
begin
TCircle(Dest).Diameter := Round(Diameter);
end;
inherited;
end;

procedure TFloatCircle.Write;
begin
Writeln('Float Circle ', Name, ' X: ', X:0:2, ' Y: ', Y:0:2,
' D: ', Diameter:0:2);
end;

end.
program DeepCopy;

{$APPTYPE CONSOLE}

uses
System.Sysutils,
System.Classes,
Shapes in 'Shapes.pas',
FloatShapes in 'FloatShapes.pas';

type
TNotShape = class(TPersistent)
protected
FName: string;
FText: string;
public
procedure Write;
procedure Assign(Source: TPersistent); override;
property name: string read FName write FName;
property Text: string read FText write FText;
end;

procedure TNotShape.Assign(Source: TPersistent);


begin
if Source is TNotShape then
begin
FName := TNotShape(Source).Name;
FText := TNotShape(Source).Text;
end
else inherited;
end;

procedure TNotShape.Write;
begin
Writeln('I am not a shape ', name, ' ', Text);
end;

var
Circle, SecondCircle, ThirdCircle: TCircle;
Square: TSquare;
FloatCircle: TFloatCircle;
Unicorn, PinkUnicorn: TUnicorn;
NotShape: TNotShape;

begin
Circle := TCircle.Create;
SecondCircle := TCircle.Create;
ThirdCircle := TCircle.Create;
Square := TSquare.Create;
FloatCircle := TFloatCircle.Create;
Unicorn := TUnicorn.Create;
PinkUnicorn := TUnicorn.Create;
NotShape := TNotShape.Create;

Circle.Name := 'Blue';
Circle.PosX := 5;
Circle.PosY := 2;
Circle.Diameter := 10;
Circle.Write;

SecondCircle.Assign(Circle);

Circle.Free;
Circle := nil;

SecondCircle.Write;

Square.Assign(SecondCircle);
Square.Write;

FloatCircle.Assign(SecondCircle);
FloatCircle.Write;

ThirdCircle.Assign(FloatCircle);
ThirdCircle.Write;

try
Unicorn.Assign(Square);
except
on E: Exception do Writeln(E.ClassName, ': ', E.Message);
end;

try
Square.Assign(Unicorn);
except
on E: Exception do Writeln(E.ClassName, ': ', E.Message);
end;

Unicorn.Name := 'Pink';
Unicorn.PosX := 1;
Unicorn.MagicSize := 3;
Unicorn.Write;

PinkUnicorn.Assign(Unicorn);
PinkUnicorn.Write;

try
NotShape.Assign(Square);
except
on E: Exception do Writeln(E.ClassName, ': ', E.Message);
end;

try
Square.Assign(NotShape);
except
on E: Exception do Writeln(E.ClassName, ': ', E.Message);
end;

SecondCircle.Free;
ThirdCircle.Free;
Square.Free;
FloatCircle.Free;
NotShape.Free;
end.

Running the above example will produce following output:


Circle Blue X: 5 Y: 2 D: 10
Circle Blue X: 5 Y: 2 D: 10
Square Blue X: 5 Y: 2 Size: 10
Float Circle Blue X: 5.00 Y: 2.00 D: 10.00
Circle Blue X: 5 Y: 2 D: 10
EConvertError: Cannot assign a TSquare to a TUnicorn
EConvertError: Cannot assign a TUnicorn to a TSquare
Unicorn Pink X: 1 Y: 0 Magic Size: 3
Unicorn Pink X: 1 Y: 0 Magic Size: 3
EConvertError: Cannot assign a TSquare to a TNotShape
EConvertError: Cannot assign a TNotShape to a TSquare

To break it down line by line:


Circle Blue X: 5 Y: 2 D: 10
Circle Blue X: 5 Y: 2 D: 10

The first two lines are the result of initializing a Circle instance and
assigning it to SecondCircle . Since both Circle and SecondCircle are instances
of the same class, they are completely identical. After we have made a
deep copy, we can release the instance referenced by Circle and we can
independently use SecondCircle .
Square Blue X: 5 Y: 2 Size: 10

The above line was printed after SecondCircle was assigned to Square . Those
instances belong to different types, so they are not clones. We merely
allowed copying data from one object instance to another based on their
class. Square is still a square, but now it represents a square that just
happens to have the same parameters as our SecondCircle .
Square.Assign(SecondCircle);
Square.Write;

Next, we copied SecondCircle to FloatCircle . They are both circles, but use
different coordinate systems. To prove that copying works in both
directions, we assigned FloatCircle to the ThirdCircle variable, which is of the
same class as SecondCircle .
Float Circle Blue X: 5.00 Y: 2.00 D: 10.00
Circle Blue X: 5 Y: 2 D: 10

FloatCircle.Assign(SecondCircle);
FloatCircle.Write;

ThirdCircle.Assign(FloatCircle);
ThirdCircle.Write;

The first exception is the result of assigning a Square to a Unicorn . The code,
as it is written, does not allow such assignments, even though TUnicorn is a
descendant of the TShape class. The code also does not allow Unicorn
assignments to Square .
EConvertError: Cannot assign a TSquare to a TUnicorn
EConvertError: Cannot assign a TUnicorn to a TSquare

try
Unicorn.Assign(Square);
except
on E: Exception do Writeln(E.ClassName, ': ', E.Message);
end;

try
Square.Assign(Unicorn);
except
on E: Exception do Writeln(E.ClassName, ': ', E.Message);
end;

The code does allow assigning one TUnicorn instance to another TUnicorn
instance:
Unicorn Pink X: 1 Y: 0 Magic Size: 3
Unicorn Pink X: 1 Y: 0 Magic Size: 3

Unicorn.Write;
PinkUnicorn.Assign(Unicorn);
PinkUnicorn.Write;

And the last two lines are the result of assigning to and from the
completely unrelated and unsupported TNotShape class. Those assignments
raise exceptions:
EConvertError: Cannot assign a TSquare to a TNotShape
EConvertError: Cannot assign a TNotShape to a TSquare

8.11 Deep copy versus shared ownership


Making deep copies is a commonly used pattern. While manual memory
management prefers exclusive ownership due to the increased
complexities involved with implementing shared ownership, memory
management is not the primary driving force behind choosing one
approach or the other.

The fundamental behavioral differences are so huge that there is no


actual comparison in terms of which one is better than the other, only
which one is more suitable for a particular use case.

To stay focused on fundamentals and not digress into the hardships of


shared ownership implementation details in the classic compiler, the
following examples and demonstrations assume usage of the ARC
compiler.

The main difference between making deep copies or sharing instances is


that with deep copying, we are free to further modify and customize each
copied object without interfering with the functionality of other objects.
With shared instances, each change in the shared object instance is
applied to all dependent objects. If we want to achieve the former, we
cannot use shared ownership. If we want to achieve the latter, shared
ownership is preferred, though we could also manually apply the desired
changes to each copy.

The ability to independently customize the important properties of an


object is the main reason why deep copying is so commonly used. More
often than not, this kind of behavior will be required.

Let's create some simple classes and observe the differences between
using deep copied objects and shared object instances. TCustomText is a
base class that implements common fields and behavior. Its descendant
classes differ in how they treat their Style property. TStyledText keeps its
own copy of the Style instance, while TUniformText uses a shared instance.
That does not mean, however, that different TUniformText instances cannot
use different shared instances of TStyle .
uses
System.SysUtils,
System.Classes;

type
TStyle = class(TPersistent)
strict protected
FColor: string;
FSize: Integer;
public
constructor Create;
procedure Assign(Source: TPersistent); override;
property Color: string read FColor write FColor;
property Size: Integer read FSize write FSize;
end;

TCustomText = class(TObject)
strict protected
FText: string;
FStyle: TStyle;
public
constructor Create(const AText: string);
procedure Write;
end;

TStyledText = class(TCustomText)
strict protected
procedure SetStyle(const Value: TStyle);
public
property Text: string read FText write FText;
property Style: TStyle read FStyle write SetStyle;
end;

TUniformText = class(TCustomText)
public
property Text: string read FText write FText;
property Style: TStyle read FStyle write FStyle;
end;

constructor TStyle.Create;
begin
inherited;
FColor := 'Black';
FSize := 10;
end;

procedure TStyle.Assign(Source: TPersistent);


begin
if Source is TStyle then
begin
FColor := TStyle(Source).Color;
FSize := TStyle(Source).Size;
end
else inherited;
end;

constructor TCustomText.Create(const AText: string);


begin
inherited Create;
FText := AText;
FStyle := TStyle.Create;
end;

procedure TCustomText.Write;
begin
Writeln(FStyle.Color, ' Size: ', FStyle.Size, ' ', FText);
end;

procedure TStyledText.SetStyle(const Value: TStyle);


begin
FStyle.Assign(Value);
end;

var
RedStyle: TStyle;
Title, Text: TStyledText;
begin
Title := TStyledText.Create('Title');
Text := TStyledText.Create('Text');

Title.Write;
Text.Write;

RedStyle := TStyle.Create;
RedStyle.Color := 'Red';
RedStyle.Size := 12;
Title.Style := RedStyle;
Text.Style := RedStyle;

Title.Write;
Text.Write;

RedStyle.Color := 'Pink';
RedStyle.Size := 8;

Title.Write;
Text.Write;

Title.Style.Color := 'Orange';

Title.Write;
Text.Write;
end.

Let's explore the TStyledText class first. We start with a default style - black
color and size 10. Next we assign RedStyle - red color, size 12 - to both the
Title and Text objects. But the main feature of deep copied instances is
seen when we change the color of RedStyle to pink and set its size to 8.
Since Text and Title have their own copies, the changes in RedStyle had
absolutely no influence. Similarly, changing Title.Style.Color to orange only
changed the appearance of Title .
Black Size: 10 Title
Black Size: 10 Text
Red Size: 12 Title
Red Size: 12 Text
Red Size: 12 Title
Red Size: 12 Text
Orange Size: 12 Title
Red Size: 12 Text

Using the same code on TUniformText shows different behavior. We start


with the default black style and assign RedStyle as before. So far
everything looks the same, but there is already an invisible difference in
place. Both Title and Text now share the RedStyle instance. That fact will
become visible with the next change. After changing the color of RedStyle
to pink and setting its size to 8, those changes will be immediately visible
on Title and Text as they both changed to pink. Changing Title.Style.Color
to orange also changed the color of Text to orange. Actually, we changed
the color of RedStyle to orange by doing that.
var
RedStyle: TStyle;
Title, Text: TUniformText;
begin
Title := TUniformText.Create('Title');
Text := TUniformText.Create('Text');
...

Black Size: 10 Title


Black Size: 10 Text
Red Size: 12 Title
Red Size: 12 Text
Pink Size: 8 Title
Pink Size: 8 Text
Orange Size: 8 Title
Orange Size: 8 Text

By adding another style, for instance BlueStyle , and assigning it to


Text.Style , Title and Text would no longer share the same Style instance.
However, if we created other TUniformText instances and assigned RedStyle or
BlueStyle to them, we would experience the same behavior as in the above
example in all TUniformText instances that share a common style.

All of the above examples can also be run in the classic compiler, but as-
is they would cause memory leaks because they do not have any of the
memory management related code required for the classic compiler.

Implementing the deep copy behavior for TStyledText in the classic compiler
is simple. To add the appropriate memory management, the TStyledText
destructor needs to be overridden and the owned FStyle field must be
released.
destructor TStyledText.Destroy;
begin
FStyle.Free;
inherited;
end;

Implementing the TUniformText class in the classic compiler would be a


more complicated task, and would involve using reference counted object
instances through interfaces or some other means of achieving shared
ownership.

While both deep copying and shared instances have their use cases
when you are dealing with scenarios that are similar to the above
mentioned styled text, imagine using shared instances to define car color
and decorations. You go crazy and paint your silver car pink and
suddenly every second car around you turns pink, too.
9 Releasing object instances
Releasing object instances is drastically different in the classic and
NextGen ARC compilers. On the surface, you can call the same methods
on both but that is where all similarities end. Underneath there are
completely different behaviors and implementations.

Method Classic compiler ARC compiler


calls the destructor Destroy protected, must never be called
Destroy
directly directly
checks instance for nil and calls
Free nils the reference
Destroy
checks instance for nil, marks it as
DisposeOf calls Free
Disposed and calls Destroy
FreeAndNil nils the reference and calls Free nils the reference

This chapter will cover only the classic compiler's behavior, coverage of
the ARC side is left for the ARC chapter.

9.1 Destroy
The destructor is the entry point for releasing any object instance. It is a
point of no return. Once the destructor is called, the object instance is on
its way to oblivion. The default destructor in Delphi is a virtual Destroy
method, implemented in TObject . It does not do anything in particular,
because TObject does not require any custom cleanup in its destructor.
TObject = class
public
...
destructor Destroy; virtual;
...
end;

destructor TObject.Destroy;
begin
end;
All the other mentioned methods for triggering object destruction will at
some point call Destroy .

Since Destroy is a virtual method, an object instance must be valid in order


to call it. Calling Destroy on a nil object reference will result in an Access
Violation exception.

Under manual memory management, directly invoking Destroy on an


object reference represents valid code. On the other hand, on ARC
platforms there is a bit more logic involved and direct invocation of Destroy
would break up that logic. Because of that, Destroy is a protected method
on ARC platforms, reserved only for internal usage and specialized
methods that handle the destruction process.

Accordingly, the following example works on the classic compiler, but it


cannot be compiled under ARC, and such code must be changed to be
cross-compiler compatible by replacing Destroy with Free .
procedure HelloDestroy;
var
sl: TStringList;
begin
sl := TStringList.Create;
try
sl.Add('Hello World');
finally
sl.Destroy;
end;
end;

But long, long, long before ARC compilers emerged rendering the above
code incompatible, there was an official recommendation to use Free
instead of Destroy - System.TObject.Destroy

Do not call Destroy directly. Call Free instead. Free verifies that the
object reference is not nil before calling Destroy.

That is one of the reasons you will rarely see the above
creation/destruction pattern in any Delphi codebase, if ever.
As to why, you will have to read further...

9.2 Free
is the foremost, fundamental method for releasing object instances,
Free
and as such it is declared in the root TObject class as a public static
method. The implementation below is specific to the classic Delphi
compiler. The ARC compiler translates calls to Free to mere nil
assignments.
TObject = class
public
...
procedure Free;
...
end;

procedure TObject.Free;
begin
if Self <> nil then
Destroy;
end;

Wait... what... shouldn't calling methods on a nil object reference result in


an Access Violation exception?

Well, not exactly. Since Free is a static method, its address is resolved at
compile time based on the variable's type and you can safely call it even
if the object reference is nil . Self is also known inside the method's
scope, even though its value may be nil . So calling Free on a nil
reference is a perfectly safe thing to do.

The ability to work without exceptions on nil references is one of the


primary reasons Free should be used instead of Destroy . Since the primary
requirement for Delphi destructors is not throwing exceptions and the
capability to cleanup partially constructed object instances - where
construction may fail before all inner object fields are constructed and
may contain nil - using Free is the only proper option. Yes, you could
always write if Assigned(FSomeObject) then FSomeObject.Destroy , but that is error
prone and not quite DRY.
That practically confines Destroy usage to releasing temporary local
objects. And even there it's acceptable only if you wrap every single local
object instance in its own try...finally block.

And the most important thing... Using Free streamlines the code. It
alleviates the burden of thinking which one is appropriate when there are
no differences in code intent between Destroy and Free . Both say that you
no longer need an object and you want to release it. The fact that the
object may or may not be nil is irrelevant at that point.

Since Free is the long-documented and preferred method for releasing


object instances it opened up the possibility of wide behavioral changes
like the one implemented in the ARC compiler, where calls to Free were
replaced with nil assignments. Such changes would still be possible even
if Free were not the recommended way for releasing object instances, but
in that case, code breakage between compilers would be more extensive
than it is now.

9.3 DisposeOf
The DisposeOf method was introduced with Delphi XE4 and the first ARC
compiler. On classic compilers, DisposeOf will just make an inline call to
Free . Its primary purpose in the classic compiler is providing cross-
compiler code compatibility.

Even though it just maps to Free on the classic compiler, DisposeOf should
not be used as a casual replacement for Free . Its usage is only
appropriate if it is required by the ARC side of the code in question.

9.4 FreeAndNil
One way of releasing objects in Delphi is passing an object reference to
the FreeAndNil procedure. Technically, it is NilAndFree because the passed
reference is first nilled and then released.
procedure FreeAndNil(var Obj);
var
Temp: TObject;
begin
Temp := TObject(Obj);
Pointer(Obj) := nil;
Temp.Free;
end;

That raises two questions. Why is it named backwards, and more


importantly, why is it implemented in such a way?

First, it is logical to name such a function FreeAndNil . If you look at the code
sequence it is meant to replace, it is exactly call Free first and then assign
nil . It is easy to assume what the function does - and the actual
background logic of nilling first and releasing second can be considered a
mere implementation detail.
Obj.Free;
Obj := nil;

As to why the actual implementation nils first and releases later, contrary
to popular belief, it is not because of thread safety. The current
implementation is not thread safe at all. Two threads can easily enter the
object destructor.

It is most likely due to the complex nature of Delphi frameworks, where


object instances are so deeply interlinked and intertwined that destroying
one object instance might trigger a series of events that will link it back to
itself and try to execute some code in the middle of the object destruction
process. Nilling the reference first can prevent such re-entry.

If you want to read more about the possible whys, Allen Bauer briefly
discussed it in his blog post, Exceptional Safety.

So far, so good. There is not much to be said about what the FreeAndNil
procedure does, it is pretty straightforward. But how and where it should
be used... well, that is something completely different. If you are feeling
bored you can always go to the Delphi forums, start a thread containing
FreeAndNil in the title, grab some popcorn and you will be entertained for
weeks to come.

FreeAndNil is a rather controversial topic. It covers everything from being


code smell, to do not ever use or you will be fired, to use always or you
will be fired.
With all the controversy and long-winded discussions going around,
FreeAndNil reached the level where it began mistakenly being used as a
universal magical solution for all kinds of memory problems.

There is nothing better for cleaning up the mess than sticking to the facts,
so I will not just plainly tell you when to use or not to use FreeAndNil (which
I eventually will...), but will also explain what FreeAndNil does and what it
does not do.
procedure DoFoo;
var
Foo: TFoo;
begin
Foo := TFoo.Create;
try
// do something with Foo
finally
Foo.Free;
end;
// accessing Foo beyond this point will result in undefined behavior
end;

The above code does not have any use for FreeAndNil . Of course, after Foo
is released and not nilled it will constitute a dangling reference, and
accessing it afterwards will result in undefined behavior.

There are several questions here. How often do you write such
erroneous code? How hard is it to spot such an error while reading the
code?

If using FreeAndNil really makes the difference, then you probably have a
different problem at hand. Keeping methods small enough to make code
easier to read and follow is a better option than using FreeAndNil just in
case.

Properly managing that single reference should not be a difficult task. If it


is, then there are some deeper design issues with that code, and it is
very likely that there are other errors in it.

9.4.1 Things to keep in mind when using FreeAndNil

1. FreeAndNil is not thread safe


2. FreeAndNil is not type safe
3. FreeAndNil nils one and only one reference
4. Using a nil reference (as in calling methods on it) does not always result in
a crash
5. Lazy initialized references should not be accessed during destruction after
they are nilled (with or without FreeAndNil)

9.4.2 FreeAndNil is not thread safe

is not a thread safe procedure. It allows two separate threads to


FreeAndNil
enter an object instance's destructor. If you have concurrency issues and
you think that FreeAndNil will solve them, think again. It will not be able to
do that.

9.4.3 FreeAndNil is not type safe

FreeAndNil is not a type safe procedure. It receives an untyped parameter,


but it only works properly if the passed parameter is an object reference.
The compiler will happily accept other types, resulting in a runtime error.
This can introduce bugs in refactored code, like when refactoring classes
from non-reference-counted to reference-counted ones, and replacing
object references with interface references.

For instance, when refactoring the following code from using TObject
var
Ref: TObject;

Ref := TObject.Create;
Ref.Free;

to using TInterfacedObject will result in compile time error

E2003 Undeclared identifier: 'Free'

var
Ref: IInterface;
Ref := TInterfacedObject.Create;
Ref.Free;

but, if we had FreeAndNil in the original code and we forgot to remove it


var
Ref: IInterface;

Ref := TInterfacedObject.Create;
FreeAndNil(Ref);

we would end up with an Access Violation exception thrown at runtime.

9.4.4 FreeAndNil nils one and only one reference

nils one and only one reference. The one you passed as a
FreeAndNil
parameter. Any other references to the object will not be nilled and will
now represent invalid - stale - references to the object.

A slightly modified stale reference example that uses FreeAndNil is all the
proof we need that only a single reference will be set to nil : the one
passed as parameter to FreeAndNil , in this case Obj . The other reference to
our object instance, Ref , will not be set to nil by that call.
program StaleFreeAndNil;

uses
System.SysUtils,
System.Classes;

procedure Proc;
var
Obj, Ref: TObject;
Owner, Child: TComponent;
begin
Obj := TObject.Create;
Ref := Obj;
Writeln('Obj created, Ref assigned');
Writeln('Obj address ', NativeUInt(Obj), ', is nil ', Obj = nil, ',
assigned ', Assigned(Obj));
Writeln('Ref address ', NativeUInt(Ref), ', is nil ', Ref = nil, ',
assigned ', Assigned(Ref));
Writeln(Ref.ToString);
FreeAndNil(Obj);
// at this point, Ref is a stale pointer
Writeln('Obj released');
Writeln('Obj address ', NativeUInt(Obj), ', is nil ', Obj = nil, ',
assigned ', Assigned(Obj));
Writeln('Ref address ', NativeUInt(Ref), ', is nil ', Ref = nil, ',
assigned ', Assigned(Ref));
Writeln(Ref.ToString);

// Some unrelated code that will overwrite memory


Owner := TComponent.Create(nil);
Child := TComponent.Create(Owner);
Writeln('Ref address ', NativeUInt(Ref), ', is nil ', Ref = nil, ',
assigned ', Assigned(Ref));
Writeln(Ref.ToString);
Owner.Free;
end;

begin
Proc;
end.

Obj created, Ref assigned


Obj address 40760912, is nil FALSE, assigned TRUE
Ref address 40760912, is nil FALSE, assigned TRUE
TObject
Obj released
Obj address 0, is nil TRUE, assigned FALSE
Ref address 40760912, is nil FALSE, assigned TRUE
TObject
Ref address 40760912, is nil FALSE, assigned TRUE
TMoveArrayManager<System.Classes.TComponent>

This is yet another reason why using FreeAndNil as some sort of magical
safety mechanism is rather useless. Keeping strict ownership and not
taking and storing unnecessary object references are the only true
mechanisms for avoiding invalid reference issues.

9.4.5 Using a nil reference (as in calling methods on it) does not
always result in a crash

This will rarely be an issue, but it has to be mentioned. Accessing object


fields, dynamic or virtual methods on a nil reference will result in an
Access Violation exception. However, calling a static method will not, and
as long as such a method does not use any instance fields or other non-
static methods it will run perfectly. Changing some global state - if we put
aside the fact that changing global state is most likely not the right thing
to do in the first place - from such methods called on a nil reference
might result in unpredictable application behavior, where you might be
thrown onto the wrong track in searching for the root cause, because you
have used FreeAndNil expecting that any further usage of nilled reference
will certainly crash the application.

9.4.6 Lazy initialized references should not be accessed during


destruction after they are nilled (with or without FreeAndNil)

This is a rather amusing side effect. At least it is if you never had to track
down such buggy code... Accessing lazy initialized references during the
destruction process can resurrect them if they are accessed after they
have been released. This will happen regardless of whether you have
used FreeAndNil or released and nilled the reference in some other way.

So you think you have killed the object and bam, a few instructions later it
has been recreated because some other code tried to do something with
it.
program LazyFreeAndNil;

uses
System.SysUtils;

type
TLazy = class(TObject)
public
constructor Create;
destructor Destroy; override;
procedure SomeLazyWork;
end;

TLazyOperator = class(TObject)
private
FLazy: TLazy;
function GetLazy: TLazy;
public
destructor Destroy; override;
procedure WorkWithLazy;
property Lazy: TLazy read GetLazy;
end;

constructor TLazy.Create;
begin
inherited;
Writeln('Lazy lives');
end;

destructor TLazy.Destroy;
begin
Writeln('Lazy destroyed');
inherited;
end;

procedure TLazy.SomeLazyWork;
begin
Writeln('Some lazy Work');
end;

destructor TLazyOperator.Destroy;
begin
FreeAndNil(FLazy);
if Lazy = nil then Writeln('Lazy is no more')
else Writeln('Alive and kicking');
inherited;
end;

function TLazyOperator.GetLazy: TLazy;


begin
if FLazy = nil then FLazy := TLazy.Create;
Result := FLazy;
end;

procedure TLazyOperator.WorkWithLazy;
begin
Lazy.SomeLazyWork;
end;

var
Worker: TLazyOperator;

begin
ReportMemoryLeaksOnShutdown := true;

Worker := TLazyOperator.Create;
try
Worker.WorkWithLazy;
finally
Worker.Free;
end;
end.
When we run the above example we will get the following output and it
will leak one TLazy instance.
Lazy lives
Some lazy work
Lazy destroyed
Lazy lives
Alive and kicking

The first three lines are expected, but the next two are not. Accidentally,
in an attempt to prove that we successfully destroyed a TLazy object
instance we directly accessed the Lazy property instead of the FLazy field.
And accessing the property automatically recreated the object.

Fixing the line if Lazy = nil then Writeln('Lazy is no more') by replacing Lazy with
FLazy in nil check will behave as expected, producing the following output:

Lazy lives
Some lazy work
Lazy destroyed
Lazy is no more

9.4.7 When you should use FreeAndNil

FreeAndNil should be used when a nil value stored in some variable is used
as a flag value, and the code logic follows different execution paths
depending on whether or not that variable contains nil . That means
FreeAndNil should be used on a reference only and only if usage of that
reference is accompanied by nil checks.

However, that does not mean that you should sprinkle your code with nil
checks to avoid accidental errors or merely to satisfy the above condition.

To rephrase, if a reference can at any point hold nil as a valid value and
depending on that flag value, determined by a nil check, you may or may
not execute some code as part of your intended workflow then you must
release such a reference using FreeAndNil or use the Free and nil
assignment sequence. FreeAndNil is not necessary only if such a
reference, once created, will remain alive as long as it remains in scope.

This is the only valid use case for FreeAndNil or any other method that nils
the reference after release.

If we take a look at the previous Lazy example, calling FreeAndNil is not


mandatory if we could guarantee that FLazy will not be accessed in any
way after being released during destruction. In other words - if we can
change that destructor code and remove access to FLazy after we
released it, then using Free would suffice.
destructor TLazyOperator.Destroy;
begin
FLazy.Free; // Free creates dangling reference we access later on
if FLazy = nil then Writeln('Lazy is no more')
else Writeln('Alive and kicking');
inherited;
end;

destructor TLazyOperator.Destroy;
begin
FLazy.Free; // we can safely use Free here
inherited;
end;

On the other hand, if we have a lazy initialized object instance that we


can also release at some point - to preserve resources - before its
owning instance ceases to exist, and if we can recreate it again on
demand, then this is a proper use case for FreeAndNil .

Now comes the tricky part...

9.4.8 Why you should not be using FreeAndNil everywhere

If you are aware of all the things FreeAndNil does not do, why not just use it
everywhere instead of Free ? You know what it does, and surely it might
occasionally save you from silly mistakes. Why is unifying the code by
using Free instead of the Destroy/Free combination a good thing, while taking
one step further and unifying the code with FreeAndNil is a bad thing?

The first reason is type safety, where you are making a tradeoff between
compile time errors and runtime errors. When type safety is concerned
there is no difference between Destroy and Free . In that regard, using
FreeAndNil solely because it can prevent you from making one silly mistake
while opening up doors for making a different silly mistake is probably not
the best reason.

The next reason, and this is the important one, is that FreeAndNil and Free
are different in intent. FreeAndNil carries additional intent and it says this
variable is meant to be reused. Or you are dealing with a complex object
instance with a convoluted destruction process and using FreeAndNil on its
inner objects can prevent issues coming from outside factors beyond
your control - moreover having such complicated destruction code can be
an indication of overall bad design.

Using FreeAndNil in the wrong places gives the wrong signals to people
reading and using the code. There are tools to be used for debugging
purposes, FreeAndNil is not such a tool. It pollutes code with unnecessary
calls that obscure the code's purpose and meaning, all for the purpose of
catching some obscure bug that might happen if the developer is not
paying attention. The time spent understanding such code can easily be
greater than the time spent once in a lifetime chasing some bug that
could, purely by chance, be found sooner if you had used FreeAndNil .

When all the above is considered, FreeAndNil brings way too few bug
protection benefits to be abused as some sort of safety net. It is not
particularly safe nor is it wide enough to catch many omissions.
10 Releasing through ownership
models
There is no magic involved when it comes to releasing object instances
through ownership models. At some point, code will still have to explicitly
call basic methods for releasing owned instances. The reason that this
kind of management deserves more detailed coverage is the fact that this
pattern is widely used not only in many Delphi core classes, but also as a
general coding pattern and developers have to know the general
principles involved in handling instances stored inside such classes, as
well as their common coding patterns.

What does the ownership model actually represent here? After all, any
object instance owns its inner fields and is responsible for managing their
memory if applicable.

The difference here is in ownership transfer. Owned object instances are


created outside of their owning object instance, but at some point we are
transferring ownership to that owning instance. On the other hand, owned
inner fields are created within their owning object instance, usually inside
constructors.

Transferring ownership keeps us from having to do the repetitive and


error-prone job of manually (writing additional code) releasing object
instances in cases where we can delegate that job.

Let's demonstrate this with an example of maintaining a collection of


objects inside an array - the simplest container.
program Arrays;

var
List: array of TObject;
Obj: TObject;
i, n: integer;

begin
ReportMemoryLeaksOnShutdown := true;
// initialize and populate the list
n := 3;
SetLength(List, n);
for i := 0 to n - 1 do
begin
Obj := TObject.Create;
List[i] := Obj;
end;

// do something with the objects in the list

// release objects contained in the list


for i := 0 to High(List) do
List[i].Free;
end.

Maintaining an array of object instances requires that we loop through the


list and manually release those instances. Writing such a loop once is not
a problem, but it is a repetitive task we would have to do every single
time we wanted to store some objects in an array.

By creating a specialized class for maintaining such lists, we can also


move the responsibility for releasing objects to that class, making our
code safer and more error-proof.

The following example shows a very basic implementation of such a list,


where we use a TArrayList instance instead of a plain array for maintaining
a list of objects. Instead of taking care of a lot of objects, cleaning them
up in a separate loop - every single time - we only have to take care of
one container instance.
program ArrayList;

type
TArrayList = class(TObject)
private
FItems: array of TObject;
function GetItem(index: Integer): TObject;
public
destructor Destroy; override;
procedure Add(Item: TObject);
procedure Clear;
property Items[index: Integer]: TObject read GetItem;
end;
destructor TArrayList.Destroy;
begin
Clear;
inherited;
end;

function TArrayList.GetItem(index: Integer): TObject;


begin
Result := FItems[index];
end;

procedure TArrayList.Add(Item: TObject);


begin
SetLength(FItems, Length(FItems) + 1);
FItems[High(FItems)] := Item;
end;

procedure TArrayList.Clear;
var
i: Integer;
begin
for i := 0 to High(FItems) do
FItems[i].Free;
SetLength(FItems, 0);
end;

var
List: TArrayList;
Obj: TObject;
i: Integer;

begin
ReportMemoryLeaksOnShutdown := true;

// initialize and populate the list


List := TArrayList.Create;
try
for i := 0 to 2 do
begin
Obj := TObject.Create;
List.Add(Obj);
end;

// do something with the objects in the list

// release the list with all contained objects


finally
List.Free;
end;
end.
10.1 Collections with ownership
Some Delphi collections implement ownership mechanisms for object
instances stored in them and take care of releasing owned objects when
the list is cleared or particular objects are removed from it. Those
collections are not the only ones capable of storing object instances, but
storing them in others - including arrays, as previously demonstrated -
requires releasing them manually, unless they serve as collections of
weak references and the instances are actually owned by some other list.

Different collections have different purposes and therefore some


variations in behavior. Since the primary focus of this book is memory
management, collections will only be briefly covered, with emphasis on
their ownership model and functionality connected with the memory
management functionality they provide.

Generally, before using any kind of collections for storing object instances
it is prudent to learn about their features and ownership model. There is a
great deal of variation of features and behavior even between the core
Delphi classes, not to mention third party libraries. Assuming specific
behavior without actually knowing how a specific collection works is the
fastest way of shooting yourself in the foot. Unless you love to live
dangerously, reading the documentation or code is mandatory.

Generally, the methods and properties implemented in collections with


ownership can be separated into two categories. First we have those that
initiate ownership transfer, both to and from a collection and then we
have those where there is no ownership transfer and the received object
references should be treated as non-owning ones. And this is just about
the most important thing you should know when using any collection
which owns stored instances.

The System.Contnrs unit contains two non-generic collections implementing


ownership mechanisms for storing object instances: TObjectList and
TComponentList . The other collections from that unit, while having Object in
their name, don't have a proper ownership model implemented. For
instance, TObjectStack and TObjectQueue as well as TObjectBucketList will not
release contained objects. Any object instance added to any of them has
to be maintained separately. However, their generic variants from the
System.Generics.Collections unit have ownership capabilities.

10.1.1 System.Contnrs.TObjectList

maintains a list of object instances; it has two constructors that


TObjectList
determine whether or not contained instances are owned. It also allows
changing its ownership model later on through its OwnsObjects property.
constructor Create; overload;
constructor Create(AOwnsObjects: Boolean); overload;

The first, parameterless constructor will set the OwnsObject property to True
and take ownership of contained objects. The second constructor allows
us to configure the OwnsObject property - passing True will have the same
effect as using the first constructor, while passing False will not maintain
ownership of contained object instances.

The value of OwnsObject is used in the Clear , Remove and RemoveItem methods. If
OwnsObject is True when the method is called, the object instance(s) will be
released. On the other hand, the Extract and ExtractItem methods will return
a specific object instance after removing it from the list, but without
releasing it - even if the collection owns the contained object instances -
this is another place where ownership transfer happens, but in this case
from the collection to the caller.

We will now take a deeper look at how the ownership model in TObjectList
works, and all the variations we can use, depending on our needs.

The following example defines a simple TNumber class, so we can more


easily track what is happening with stored object instances. Also, turning
on ReportMemoryLeaksOnShutdown will report if our code has created any memory
leaks.

For a start, three freshly created number objects will be added to the list
in a loop, then we will output the number of objects in the list, and then
iterate through the list and write the values stored in each object
instance.
program ObjectList;
uses
System.Classes,
System.Contnrs;

type
TNumber = class(TObject)
public
Value: integer;
constructor Create(ANumber: integer);
end;

constructor TNumber.Create(ANumber: integer);


begin
inherited Create;
Value := ANumber;
end;

procedure WriteList(AList: TObjectList);


var
i: integer;
Number: TNumber;
begin
Writeln('Count: ', AList.Count);
for i := 0 to AList.Count - 1 do
begin
Number := TNumber(AList.Items[i]);
Writeln('Number at position: ', i, ' is ', Number.Value);
end;
end;

var
List: TObjectList;
Number: TNumber;
i: integer;

begin
ReportMemoryLeaksOnShutdown := true;

List := TObjectList.Create;
try
for i := 0 to 2 do
begin
Number := TNumber.Create(i + 1);
List.Add(Number);
end;

WriteList(List);
finally
List.Free;
end;
end.

Running the code will produce the following output and no memory leaks
will be reported:
Count: 3
Number at position: 0 is 1
Number at position: 1 is 2
Number at position: 2 is 3

Let's analyze the code, step by step.

The following piece of code creates and releases our List of objects. The
code calls the parameterless constructor, which implies ownership of the
objects added to the list. That is why there are no memory leaks, even
though we never explicitly call Free on any Number object instance.
TObjectList 's destructor calls the Clear method that will clear the list and
handle owned objects - if they are owned.
List := TObjectList.Create;
try
...
finally
List.Free;
end;

Creating List with the other constructor and passing False to its AOwnsObjects
parameter will result in three leaked TNumber instances.
List := TObjectList.Create(false);

We can also create a leak by using the parameterless constructor and


flipping the OwnsObjects property at a later time. It does not matter whether
we set it before or after we add objects to the list. To reproduce the leak,
we just need to set it before any ownership handling ( Remove , RemoveItem ,
Clear ) methods are called - in this case, that will be the Clear call from the
destructor.
List := TObjectList.Create;
try
...
List.OwnsObjects := false;
...
finally
List.Free;
end;

Now, let's take a look at the first loop that creates and adds numbers to
the list.
for i := 0 to 2 do
begin
Number := TNumber.Create(i + 1);
List.Add(Number);
end;

The line Number := TNumber.Create(i + 1); is pretty straightforward - here we


create a TNumber instance initialized with our loop index variable (+1 is here
just to make a distinction between the object's index and the value it
contains) and assign it to the variable Number .

By calling List.Add(Number); we are transferring ownership of the object


instance referenced by Number to the List . At that point there are two
references to that instance: one in the variable Number and another in the
List 's array field, which stores object references added to the list.

We can still use Number to access the object instance since it is a valid
reference, but since we have transferred ownership, any changes in the
List might convert our Number into a stale reference.

For instance, the following sequence would result in accessing a stale


reference:
List := TObjectList.Create;
...
Number := TNumber.Create(5);
List.Add(Number);
List.Clear;

// At this point Number is a stale reference


Writeln(Number.Value);

Back to the loop. After we added the object instance to the list, we can
reuse the Number variable for storing another TNumber instance created in the
next loop iteration.
One of the common mistakes made by developers not yet fully
acquainted with manual memory management is calling Free after adding
Number to the list, because they have been told to release objects they
have created, but they are not aware they have transferred ownership of
the object to the list. The following code would result in a crash when we
call List.Free . In the meantime, any attempt to access objects stored in the
list may or may not seem to work properly, depending on the code.
for i := 0 to 2 do
begin
Number := TNumber.Create(i + 1);
List.Add(Number);
// THIS IS WRONG
// This line would result in stale references inside TList
Number.Free;
end;

If we combine the above erroneous code with a list that does not hold
ownership of the object instances, we can create a non-crashing, non-
leaking example that produces the wrong output.
List := TObjectList.Create(false);
try
for i := 0 to 2 do
begin
Number := TNumber.Create(i + 1);
List.Add(Number);
Number.Free;
end;

WriteList(List);
finally
List.Free;
end;

And instead of the correct output (the one from the original example) we
would get
Count: 3
Number at position: 0 is 3
Number at position: 1 is 3
Number at position: 2 is 3

What happened here?


In each loop iteration we have created a new object instance, but since
we also release that object instance in that iteration, the Delphi memory
manager will allocate memory for each new instance at the same place
on the heap - reusing the first available slot. Effectively, that means the
same memory address is stored in the list over and over again. That is
why printing the contents of the list will show the contents of the last
added instance. Actually, that instance is no longer valid at the time of
printing, but since we haven't allocated any new objects in the meantime,
using it will appear to work properly.

The following example will show how the ownership model works when
we remove or extract items from the list. We will fill the list with number
objects as before, and then we will remove odd numbers from the list.
Since we are removing items from the list while iterating through it, the
second loop will iterate through the list backwards, or we would have to
use a while loop.

The Remove method also releases the object instance if the list holds
ownership, so there will be no memory leaks.
List := TObjectList.Create;
try
for i := 0 to 2 do
begin
Number := TNumber.Create(i + 1);
List.Add(Number);
end;

WriteList(List);

for i := List.Count - 1 downto 0 do


begin
Number := TNumber(List.Items[i]);
if Odd(Number.Value) then List.Remove(Number);
end;

WriteList(List);
finally
List.Free;
end;

Count: 3
Number at position: 0 is 1
Number at position: 1 is 2
Number at position: 2 is 3
Count: 1
Number at position: 0 is 2

If we wanted to move odd numbers to another list, transferring ownership


to that one, we would use the Extract method instead. To preserve the
order of the numbers in OddList , a while loop will be used instead of a for
loop.
var
List, OddList: TObjectList;
Number: TNumber;
i: integer;

begin
ReportMemoryLeaksOnShutdown := true;

OddList := nil;
List := TObjectList.Create;
try
OddList := TObjectList.Create;

for i := 0 to 2 do
begin
Number := TNumber.Create(i + 1);
List.Add(Number);
end;

WriteList(List);

i := 0;
while i < List.Count do
begin
Number := TNumber(List.Items[i]);
if Odd(Number.Value) then OddList.Add(List.Extract(Number))
else Inc(i);
end;

WriteList(List);
WriteList(OddList);
finally
OddList.Free;
List.Free;
end;
end.

Count: 3
Number at position: 0 is 1
Number at position: 1 is 2
Number at position: 2 is 3

Count: 1
Number at position: 0 is 2

Count: 2
Number at position: 0 is 1
Number at position: 1 is 3

If we want to maintain a list of odd numbers as addition to a complete list


of numbers, and we don't want to move numbers from the first list to the
other, we would create a secondary list as a non-owning one. In such
cases we have to take care that such a list is released before (or at least
that items in the list are not accessed after the owning list is released) the
one holding ownership over the objects because the secondary list
contains stale references at that point.
OddList := nil;
List := TObjectList.Create;
try
OddList := TObjectList.Create(false);

for i := 0 to 2 do
begin
Number := TNumber.Create(i + 1);
List.Add(Number);
end;

WriteList(List);

for i := 0 to List.Count - 1 do
begin
Number := TNumber(List.Items[i]);
if Odd(Number.Value) then OddList.Add(Number);
end;

WriteList(List);
WriteList(OddList);
finally
OddList.Free;
List.Free;
end;

Count: 3
Number at position: 0 is 1
Number at position: 1 is 2
Number at position: 2 is 3

Count: 3
Number at position: 0 is 1
Number at position: 1 is 2
Number at position: 2 is 3

Count: 2
Number at position: 0 is 1
Number at position: 1 is 3

10.1.2 System.Contnrs.TComponentList

TComponentList behaves just like TObjectList , with two distinctions. It works


with TComponent and TComponent -derived object instances and it uses the
notification system of TComponent to discover when any of the contained
components is released. In such an event, it automatically removes the
component from the list.
program ComponentList;

uses
System.SysUtils,
System.Classes,
System.Contnrs;

procedure WriteList(AList: TComponentList);


var
i: integer;
Component: TComponent;
begin
Writeln('Count: ', AList.Count);
for i := 0 to AList.Count - 1 do
begin
Component := AList.Items[i];
Writeln('Component at position: ', i, ' is ', Component.Name);
end;
Writeln;
end;

var
List: TComponentList;
Component: TComponent;
i: integer;

begin
ReportMemoryLeaksOnShutdown := true;
List := TComponentList.Create;
try
for i := 0 to 2 do
begin
Component := TComponent.Create(nil);
Component.Name := 'Component' + IntToStr(i + 1);
List.Add(Component);
end;
WriteList(List);

// calling Free is allowed for components because list will be notified


Component.Free;
WriteList(List);
finally
List.Free;
end;

Readln;
end.

Running the above example will produce the following output and no
leaks, even though we have created components without an owner.
Adding components to the list will take ownership over the component.
Calling Free explicitly on any component in the list will notify the list of its
removal and there would be no stale references left in the list.
Count: 3
Component at position: 0 is Component1
Component at position: 1 is Component2
Component at position: 2 is Component3

Count: 2
Component at position: 0 is Component1
Component at position: 1 is Component2

It is important to remember that the notification system works only if we


store components in TComponentList . If we replace TComponentList in the above
example with TObjectList we would access a stale reference and crash.
program ComponentListCrash;

uses
System.Classes,
System.Contnrs;

var
List: TObjectList;
Component: TComponent;
i: integer;

begin
ReportMemoryLeaksOnShutdown := true;

List := TObjectList.Create;
try
for i := 0 to 2 do
begin
Component := TComponent.Create(nil);
List.Add(Component);
end;
Component.Free;
finally
List.Free;
end;
end.

In the above example, a crash will happen at the List.Free line, but only
because the list contains a stale reference to a component we explicitly
released with Component.Free .

The System.Generics.Collections unit contains various generic collection types


suitable for storing both value and reference types. Implemented
collections are cross-compiler compatible, and should be used in cross-
compiler code instead of the ones implemented in the System.Contnrs unit.
They are also type-safe, more versatile and feature-rich.

10.1.3 System.Generics.Collections.TObjectList<T>

This is a generic variant of TObjectList with a very similar workflow


regarding ownership. It also has a OwnsObjects property that can be set
through constructor and modified later on. It is a more versatile class than
TObjectList , with a greater variety of methods for adding and removing
object instances. However, the same ownership transfer principles apply.
The methods Clear , Delete , DeleteRange , Remove and RemoveItem use the OwnsObjects
property and release object instances accordingly, while the Extract and
ExtractItem methods don't release object instances regardless of the
OwnsObjects property.
The following example is a generic equivalent of the TObjectList example
where we move odd numbers to a separate list. The code itself is very
similar, the only differences are in the generic list's declaration and
construction. And another one is that we no longer need to use hard
typecasting of items to TNumber because the generic variant of TObjectList
does not operate on TObject instances, but on a specific type we use while
declaring the generic list variable.
program GenericObjectListExtract;

uses
System.Classes,
System.Generics.Collections;

// TNumber declaration and implementation


...

procedure WriteList(AList: TObjectList<TNumber>);


var
i: integer;
Number: TNumber;
begin
Writeln('Count: ', AList.Count);
for i := 0 to AList.Count - 1 do
begin
Number := AList.Items[i];
Writeln('Number at position: ', i, ' is ', Number.Value);
end;
Writeln;
end;

var
List, OddList: TObjectList<TNumber>;
Number: TNumber;
i: integer;

begin
ReportMemoryLeaksOnShutdown := true;

OddList := nil;
List := TObjectList<TNumber>.Create;
try
OddList := TObjectList<TNumber>.Create;

for i := 0 to 2 do
begin
Number := TNumber.Create(i + 1);
List.Add(Number);
end;

WriteList(List);

i := 0;
while i < List.Count do
begin
Number := List.Items[i];
if Odd(Number.Value) then OddList.Add(List.Extract(Number))
else Inc(i);
end;

WriteList(List);
WriteList(OddList);
finally
OddList.Free;
List.Free;
end;
end.

10.1.4 System.Generics.Collections.TObjectStack<T>

This is a generic stack implementation with an ownership model for


object instances. Like other collections with ownership models, it also has
OwnsObjects property that can be set through the constructor and changed
at a later time.

If the stack owns contained object instances, all instances currently


contained in the stack during its release will also be released.

Using the Push method transfers ownership of the object instance to the
stack. After the instance is pushed it may become a stale reference if the
contents of the stack are altered in any way. So we have to be careful
about how we use such a reference later on. The best practice is not to
use them at all after we have transferred ownership. By doing so, we
reduce the chance of accidental errors in subsequent code changes.

There are several methods for retrieving objects pushed to the stack. Peek
will return the top object instance but without removing it from the stack -
the stack will still hold ownership over it. Such a peeked reference should
be used locally and with care, because it will be valid only for as long as
the stack is not altered. Pop - will just remove the top instance, and
release it if the stack owns instances. And Extract will return the top
instance, removing it from the stack, but without releasing it. We have to
release the returned value manually.
program GenericStack;

uses
System.Classes,
System.Generics.Collections;

// TNumber declaration and implementation


...

var
Stack: TObjectStack<TNumber>;
Number: TNumber;
i: integer;

begin
ReportMemoryLeaksOnShutdown := true;

Stack := TObjectStack<TNumber>.Create;
try
for i := 0 to 2 do
begin
Number := TNumber.Create(i + 1);
Stack.Push(Number);
end;
Writeln('Count: ', Stack.Count);

Number := Stack.Extract;
Writeln('Count: ', Stack.Count);
Writeln('Extracted number is: ', Number.Value);
Number.Free;

Stack.Pop;
Writeln('Count: ', Stack.Count);
finally
Stack.Free;
end;
end.

Count: 3
Count: 2
Extracted number is: 3
Count: 1

10.1.5 System.Generics.Collections.TObjectQueue<T>
Just as the generic stack has a variant with ownership, so does the
generic queue. Again, with an OwnsObjects property configurable through the
constructor. Just as with stacks, if a queue owns contained instances
they will be released when the queue gets released.

Using the Enqueue method, we add an object instance and transfer its
ownership to the queue. The same rules apply for making sure we don't
use added objects after we transferred ownership to the queue to avoid
using stale references.

The queue also has several methods for retrieving objects. Peek will return
the head object instance but without removing it from the queue - the
queue will still hold ownership on it. Dequeue - will just remove the head
instance and release it if the queue owns instances. And Extract will return
the head instance, removing it from the queue, but without releasing it.
We have to release the returned value manually.
program GenericQueue;

uses
System.Classes,
System.Generics.Collections;

// TNumber declaration and implementation


...

var
Queue: TObjectQueue<TNumber>;
Number: TNumber;
i: integer;

begin
ReportMemoryLeaksOnShutdown := true;

Queue := TObjectQueue<TNumber>.Create;
try
for i := 0 to 2 do
begin
Number := TNumber.Create(i + 1);
Queue.Enqueue(Number);
end;
Writeln('Count: ', Queue.Count);

Number := Queue.Extract;
Writeln('Count: ', Queue.Count);
Writeln('Extracted number is: ', Number.Value);
Number.Free;

Queue.Dequeue;
Writeln('Count: ', Queue.Count);

finally
Queue.Free;
end;
end.

Count: 3
Count: 2
Extracted number is: 1
Count: 1

10.1.6
System.Generics.Collections.TObjectDictionary<TKey,TValue>

Ownership in a dictionary is a bit more complex. Since


TObjectDictionary<TKey,TValue> allows taking ownership of either keys or
values, the boolean OwnsObjects property would not suffice. Instead,
TObjectDictionary<TKey,TValue> has the FOwnerships field declared as a set -
TDictionaryOwnerships . TObjectDictionary<TKey,TValue> does not allow changing its
ownership model later on, and Ownerships passed through the constructor
will be used during the lifetime of the dictionary instance. If Ownerships is not
specified during dictionary construction, the default value will be an
empty set.
TDictionaryOwnerships = set of (doOwnsKeys, doOwnsValues);

As with other collections, ownership is transferred by adding a key/value


pair to the dictionary using the Add or AddOrSetValue methods. Since the
dictionary maintains a single value per key, the old value will be released
if the dictionary has ownership of its values. Using Clear or Remove will
release both the keys and the values, depending on the ownership
model. On the other hand, the ExtractPair method will remove a key/value
pair from the dictionary without releasing it, and we will have to release it
manually.

The following example will show basic TObjectDictionary usage.


program GenericDictionary;
uses
System.Classes,
System.Generics.Collections;

// TNumber declaration and implementation


...

procedure WriteDictionary(Dictionary: TObjectDictionary<string, TNumber>);


var
Key: string;
Number: TNumber;
begin
Writeln('Count: ', Dictionary.Count);
for Key in Dictionary.Keys do
begin
Number := Dictionary.Items[Key];
Writeln('Key: ' + Key, ' contains value: ', Number.Value);
end;
Writeln;
end;

var
Dictionary: TObjectDictionary<string, TNumber>;
Number: TNumber;
Pair: TPair<string, TNumber>;
begin
ReportMemoryLeaksOnShutdown := true;

Dictionary := TObjectDictionary<string, TNumber>.Create([doOwnsValues]);


try
Number := TNumber.Create(1);
Dictionary.Add('one', Number);

Number := TNumber.Create(2);
Dictionary.Add('two', Number);

Number := TNumber.Create(3);
Dictionary.Add('three', Number);

WriteDictionary(Dictionary);

Number := TNumber.Create(33);
Dictionary.AddOrSetValue('three', Number);

Dictionary.Remove('two');

WriteDictionary(Dictionary);

Pair := Dictionary.ExtractPair('one');
Pair.Value.Free;

WriteDictionary(Dictionary);
finally
Dictionary.Free;
end;
end.

Count: 3
Key: one contains value: 1
Key: two contains value: 2
Key: three contains value: 3

Count: 2
Key: one contains value: 1
Key: three contains value: 33

Count: 1
Key: three contains value: 33

In the above example, the dictionary will only take ownership of its
values, because the keys are declared as string and are not a class type.
Taking ownership of any non-class type will result in an EInvalidCast
exception raised at runtime. You can test that by replacing the above
dictionary construction with the following code:
Dictionary := TObjectDictionary<string, TNumber>.Create([doOwnsKeys, doOwnsValu

can have any combination of types used as


TObjectDictionary<TKey,TValue>
keys and values. As long as you take ownership of any class types and
don't take ownership of any non-class types, it will work properly without
any leaks.

Even if you use class types for keys or values, you don't have to maintain
ownership through the dictionary. But in that case, all object instances
added to the dictionary must be handled either manually, or by storing
them in some other collection that will take care of their release by
owning them.

Using a TObjectDictionary<TKey,TValue> where keys are owned by the dictionary


and you have a custom comparer defined is a slightly more complicated
scenario. The problem is in the AddOrSetValue method. If you create a brand
new key object and the dictionary already contains an entry where the
existing key is deemed equal by the comparer even though it is a
different object, that new key object will leak. If the key already exists,
AddOrSetValue will call the DoSetValue method, which will replace the old value
with the new one and if the values are owned that will release the old
value. But it will do nothing related to the key.

Running the following example will leak a single TKeyNumber instance, the
one we tried to add with AddOrSetValue . If we create that key with a non-
existent value, like 13, TKeyNumber.Create(13) , there will be no leak.
program GenericDictionaryKeys;

uses
System.Classes,
System.Generics.Defaults,
System.Generics.Collections;

type
TNumber = class(TObject)
public
Value: integer;
constructor Create(ANumber: integer);
end;

TKeyNumber = class(TNumber);

constructor TNumber.Create(ANumber: integer);


begin
inherited Create;
Value := ANumber;
end;

procedure Test;
var
Dictionary: TObjectDictionary<TKeyNumber, TNumber>;
Key: TKeyNumber;
Number: TNumber;
Comparer: IEqualityComparer<TKeyNumber>;
i: integer;
begin
Comparer := TDelegatedEqualityComparer<TKeyNumber>.Create(

function(const Left, Right: TKeyNumber): Boolean


begin
Result := Left.Value = Right.Value;
end,
function(const Key: TKeyNumber): Integer
begin
Result := Key.Value;
end);

Dictionary := TObjectDictionary<TKeyNumber, TNumber>.Create(


[doOwnsKeys, doOwnsValues], Comparer);
try
for i := 0 to 10 do
begin
Key := TKeyNumber.Create(i);
Number := TNumber.Create(i);
Dictionary.Add(Key, Number);
end;

Key := TKeyNumber.Create(3);
Number := TNumber.Create(33);
Dictionary.AddOrSetValue(Key, Number);

finally
Dictionary.Free;
end;
end;

begin
ReportMemoryLeaksOnShutdown := true;
Test;
end.

So, how to update the above code to avoid key leaks?

We have to replace
Dictionary.AddOrSetValue(Key, Number);

with
if Dictionary.ContainsKey(Key) then
begin
Dictionary.AddOrSetValue(Key, Number);
Key.Free;
end
else Dictionary.Add(Key, Number);

If Dictionary already contains the Key , we can release our newly created -
duplicate - Key after we add Number to the Dictionary .
There is another thing to keep in mind when two objects are created to
be added. Standard construction protection with a twist - since we will
use a try...except block with a re-raising exception. If constructing the
second object fails - we have to release the first one or it will leak.
Key := TKeyNumber.Create(i);
try
Number := TNumber.Create(i);
except
Key.Free;
raise;
end;

And that is not all... there are more possible leaks in the above code...
ones caused by ownership transfer failure that can happen not just in a
dictionary, but in any collection. Coming up next... but before we dive into
such exception handling, let's cover another core collection type.

10.1.7 System.Classes.TCollection

TCollection maintains a collection of TCollectionItem descendants. TCollection is


compatible with the Delphi component streaming system, and allows
collections of items to be stored in forms, frames and data modules.
TCollection does not implement a configurable ownership model, instead it
unconditionally owns all contained items.

and particularly TCollectionItem are meant to be extendable base


TCollection
classes. TCollection 's constructor receives a parameter of TCollectionItemClass
that is used for creating and adding new items into the collection.

Similarly to TObjectList , the base TCollection class operates on TCollectionItem


instances, so some typecasting is necessary in order to access a specific
descendant's functionality. Usually, along with implementing custom
TCollectionItem s, an appropriate custom TCollection class is also created to
avoid unnecessary typecasts and provide more type safety when dealing
with a particular collection.
TCollection = class(TPersistent)
private
FItemClass: TCollectionItemClass;
FItems: TList<TCollectionItem>;
...
public
constructor Create(ItemClass: TCollectionItemClass);
function Add: TCollectionItem;
function Insert(Index: Integer): TCollectionItem;
procedure Clear;
procedure Delete(Index: Integer);
...
end;

The functions Add and Insert are actually factory functions that construct a
new collection item, add it to the collection, and return it as result,
primarily for customization purposes. There is no external ownership
transfer here, as collection items are automatically added to the collection
within the collection instance. This happens strictly internally as TCollection
delegates maintaining its items to a generic list.

clears the whole collection, releasing contained items, and Delete


Clear
removes and releases an item at the specified index.

At first glance it seems that you cannot move items between collections
or even extract them, but that functionality is publicly available through
TCollectionItem , which has a Collection property pointing back to its owner
collection. By changing that property, we can either move an item to
another collection or extract it from the collection if we set Collection to nil .
The extracted item has to be manually released or a memory leak will
occur.

The following example shows basic usage of the mentioned methods and
properties.
program Collection;

uses
System.SysUtils,
System.Classes;

type
TNumberItem = class(TCollectionItem)
protected
function GetDisplayName: string; override;
public
Value: Integer;
end;
function TNumberItem.GetDisplayName: string;
begin
Result := IntToStr(Value);
end;

procedure WriteList(AList: TCollection);


var
i: integer;
Item: TCollectionItem;
begin
Writeln('Count: ', AList.Count);
for i := 0 to AList.Count - 1 do
begin
Item := AList.Items[i];
Writeln('Item at position: ', i, ' is ', Item.DisplayName);
end;
Writeln;
end;

var
List: TCollection;
Item: TCollectionItem;
i: integer;

begin
ReportMemoryLeaksOnShutdown := true;

List := TCollection.Create(TNumberItem);
try
for i := 0 to 2 do
begin
TNumberItem(List.Add).Value := i + 1;
end;
WriteList(List);

List.Delete(1);
WriteList(List);

Item := TNumberItem(List.Items[0]);
Item.Collection := nil;
WriteList(List);
Item.Free;
finally
List.Free;
end;
end.

Count: 3
Item at position: 0 is 1
Item at position: 1 is 2
Item at position: 2 is 3

Count: 2
Item at position: 0 is 1
Item at position: 1 is 3

Count: 1
Item at position: 0 is 3

10.2 Handling exceptions during ownership transfer


There is one important aspect of ownership transfer you must be aware
of. Adding objects to any kind of collection can fail - if the internal array
cannot grow to accept additional items, an out-of-memory exception will
be raised. In that case, the object we wanted to add to the collection will
not be added, the ownership transfer will not be completed and if we
don't release that object manually, it will leak. This is only true for the
classic compiler, ARC would take care of such a stranded object - yet
another advantage of automatic memory management, where you don't
have to take care of all the tiny details that are irrelevant in most cases.

To prevent a leak, we have to protect adding an object to the collection


with a try...except block, and in the event of an exception, release the
object and re-raise the exception.

To keep collection examples in this chapter as simple as possible, none


of the examples protects item addition with a try...except block. To do so,
you have to use the following pattern:
Item := TItem.Create(...);
try
// Item initialization
...
// call any method that transfers ownership to the collection
// Add, Insert, Push, Enqueue...
List.Add(Item);
except
Item.Free;
raise;
end;

Or specifically, the corrected example for TObjectList<T> would be


List := TObjectList<TNumber>.Create;
try
for i := 0 to 2 do
begin
Number := TNumber.Create(i + 1);
try
List.Add(Number);
except
Number.Free;
raise;
end;
end;
finally
List.Free;
end;

The above code is technically, absolutely the most correct code you can
write. Still, you will find that such situations are not handled properly in
many code-bases.

Why?

It all depends on the what a particular piece of code is doing and where.
Inability to add an object to the list due to an out-of-memory exception is
a pretty serious issue. It usually means something is horribly wrong, the
application is going to hell anyway and taking care of a small leak seems
like an exercise in futility. And an additional try...except block does not
improve code readability.

However, if this kind of code is part of some framework or library, then it


is prudent to properly clean up all the leaks and re-raise the exception.
You never know what the logic of the code that uses the framework is,
and whether or not it is possible for some application to fully recover from
such an error. Delphi applications can continue to work properly even
after horrible exceptions, but that depends on the circumstances.

Let's say the code in question is part of an XML parser in some


application. Everything works fine until someone tries to feed an overly
large XML file into the application. And bang... there is not enough
memory to process that file.

If the parser function properly cleans up all the pieces, and the
application captures and handles the exception, it can be a fully
recoverable error. The application can just show the user that that
particular file could not be processed because it is too large, and that is it.
It does not have to go down in flames, it can continue to function
perfectly, fully capable of parsing some smaller files.

If the parser function did not properly clean up memory, that small object
it allowed to leak could end up sitting in the middle of the heap, causing
memory fragmentation. Repeated leaks can severely limit the
application's ability to function properly until even much smaller files can
no longer be successfully processed.

Cleaning up after a failed ownership transfer should be done in a similar


fashion, regardless of the collection class we are using - that includes
protecting any method that inserts, enqueues, pushes or in any other way
adds an object instance and transfers ownership to the collection.

After having one eggnog too many, feeling exceptionally merry, you make
a New Year's resolution. You will clear every possible leak in your code.
You are not going to be a sloppy programmer. And you start digging into
your code, adding all the missing try...except blocks.

And then you bump into this one. Grumble... this is getting more and
more convoluted.
i := 0;
while i < List.Count do
begin
Number := List.Items[i];
if Odd(Number.Value) then OddList.Add(List.Extract(Number))
else Inc(i);
end;

Do you add the object back? Do you add it first and then extract? Do you
just Free the darn thing?
i := 0;
while i < List.Count do
begin
Number := List.Items[i];
if Odd(Number.Value) then
begin
OddList.Add(Number);
List.Extract(Number);
end
else Inc(i);
end;

i := 0;
while i < List.Count do
begin
Number := List.Items[i];
if Odd(Number.Value) then
try
OddList.Add(List.Extract(Number));
except
Number.Free;
raise;
end
else Inc(i);
end;

Adding the object back makes no sense. And when choosing between an
add-extract sequence and catching the exception, the former produces
cleaner code. Cleaner??? Well... cleaner in the sense that there are
fewer lines of code. Its intent, on the other hand, may be not so clear.
Anyone reading that code would have to be aware that you are trying to
avoid a memory leak in the event of a total calamity, so adding a
comment to the code might be wise thing to do.

Next... TObjectDictionary<TKey, TValue>

First, a simple situation when only values are owned by the dictionary.
This is pretty much the same situation as with a plain object list. Both Add
or AddOrSetValue can fail and we have to avoid the leak by releasing the
value instance inside a try...except block. Basically, what we should do
with any other collection using any other similar method that adds an
instance to any collection. But, let's repeat that code anyway.
Number := TNumber.Create(1);
try
Dictionary.Add('one', Number);
except
Number.Free;
raise;
end;
If we want to extract a value from one dictionary and add it to another
dictionary or collection we will be in a situation similar to the previously-
mentioned example of moving an object from one list to another.

Dictionaries that also own their keys are the really problematic ones. We
already covered a situation where Key must be released if owned by the
dictionary, but that code didn't handle possible exceptions during
ownership transfer. The following code handles them all. If you think that
execution of a branch where Key is already contained in the collection
cannot fail and that you don't have to protect AddOrSetValue in this particular
situation, you are wrong. Under the right circumstances it can also fail,
because that method will first try to grow the dictionary, and then it will
use the appropriate code for adding the value depending on whether the
key already exists.
Key := TKeyNumber.Create(i);
try
Number := TNumber.Create(i);
except
Key.Free;
raise;
end;

if Dictionary.ContainsKey(Key) then
try
try
Dictionary.AddOrSetValue(Key, Number);
except
Number.Free;
raise;
end
finally
Key.Free;
end
else
try
Dictionary.Add(Key, Number);
except
Key.Free;
Number.Free;
raise;
end;

If you think the above is a rather convoluted piece of code, trust me, you
are not the only one.
When it comes to the TCollection class, the situation is slightly different.

The TCollectionItem constructor, called by the Add or Insert methods of the


collection, takes the owning collection as a parameter. If the collection
cannot grow to accommodate a new item, an out-of-memory exception
will be raised inside the TCollectionItem constructor, automatically triggering
the destruction of that item. So there is no need to manually release such
an item to avoid a memory leak.
TCollectionItem = class(TPersistent)
...
public
constructor Create(Collection: TCollection); virtual;
...

As you might guess, using the Collection property to extract an item from a
collection is a perfectly safe operation, just like any other extraction,
however using Collection to move an item into another collection is not.

The SetCollection method used to change the Collection property has the
following implementation:
procedure TCollectionItem.SetCollection(Value: TCollection);
begin
if FCollection <> Value then
begin
if FCollection <> nil then FCollection.RemoveItem(Self);
if Value <> nil then Value.InsertItem(Self);
end;
end;

If InsertItem in the above code fails, the collection item's ownership transfer
will not be completed and we will have to take care of that item to prevent
it from leaking.
Item := FirstCollection.Items[0];

try
Item.Collection := SecondCollection;
except
Item.Free;
raise;
end;
Avoiding such ownership transfer leaks under manual memory
management usually implies knowing the internals of the used classes.
Internals you normally should not have to manage. Once again, a deeper
knowledge of the used libraries and frameworks is the only way to
prevent issues that must be solved in your own code.

When you deal with any kind of ownership transfer, no matter how
innocent your side of the code looks, it is the code the behind the scenes
that might fail and cause you some troubles.

This brings us back to your code-fixing story which has two possible
endings, neither of which are exceptionally happy...

You don't fulfill your New Year's resolution - who can blame you?
You fulfill your New Year's resolution and make a new one, never to make
such a stupid resolution again!

In any case, you decide ARC is not such a bad thing after all and you
become an avid proponent ;-)

10.3 TComponent ownership


The TComponent class is one of the core Delphi classes which implements
direct ownership, and is suitable for creating and managing object
hierarchies from one root object instance. Along with the TComponent
streaming system, this constitutes the base upon which Delphi visual
frameworks are built.

Each TComponent object instance is constructed with an Owner responsible for


its release. If a TComponent instance's Owner is nil , such an instance will have
no owner and it must be manually released using the regular methods for
releasing object instances like Free or FreeAndNil , depending on the use
case. The ARC compiler has slightly different requirements, and TComponent
and its descendants require DisposeOf to be properly released, but that
requirement is covered in the ARC chapter.

Each TComponent object instance can own any number of other components
maintained in the internal FComponents list. The accompanying public read-
only properties ComponentCount and Components can be used for iterating
through owned components. When a component is destroyed, all
components in its FComponents list will also be destroyed.

A cut-down declaration of TComponent regarding its ownership mechanisms


is as follows:
type
TComponent = class(TPersistent, IInterface, IInterfaceComponentReference)
private
// [Unsafe] is here to prevent strong reference cycles in ARC compiler
// it has no impact in the classic compiler
[Unsafe] FOwner: TComponent;
FComponents: TList<TComponent>;
public
constructor Create(AOwner: TComponent); virtual;

procedure InsertComponent(const AComponent: TComponent);


procedure RemoveComponent(const AComponent: TComponent);

property Owner: TComponent read FOwner;

property Components[Index: Integer]: TComponent read GetComponent;


property ComponentCount: Integer read GetComponentCount;
end;

The Owner of the component is set in the constructor and is a read-only


property. On first sight, it may look like you cannot change the
component's owner once it is constructed. However, TComponent implements
two methods - InsertComponent and RemoveComponent - that can be used to
change the owner of the component.

If we look at the TComponent constructor, we will find that ownership of the


component is transferred to the passed AOwner parameter by calling
InsertComponent

if AOwner <> nil then AOwner.InsertComponent(Self);

and the corresponding code in the destructor that removes the


component from its owner's list is
if FOwner <> nil then FOwner.RemoveComponent(Self);

Since a component can only have a single owner, InsertComponent will


ensure that by calling RemoveComponent on its current owner first, and then it
will take ownership of the component and add it to the list of owned
components. Just like any other ownership transfer that requires growing
a list, in this case a list of owned components, it can fail to allocate
memory and raise an out-of-memory exception. To properly clean up
memory, we have to protect that code with a try...except block and release
the component if the ownership transfer fails.
var
Child, Owner, NewOwner: TComponent;
...
// we can also create a Child component without an owner
Child := TComponent.Create(Owner);

try
NewOwner.InsertComponent(Child);
except
Child.Free;
raise;
end;

There are two common patterns used when constructing and releasing a
temporary component:
procedure TFooComponent.DoSomething;
var
Local: TComponent;
begin
Local := TComponent.Create(nil);
try
// do something with component
finally
Local.Free;
end;
end;

procedure TFooComponent.DoSomething;
var
Local: TComponent;
begin
Local := TComponent.Create(Self);
try
// do something with component
finally
Local.Free;
end;
end;
The first pattern, the one without an owner, is preferred because it clearly
indicates the intent that the component is just a temporary local one. It
also skips unnecessary adding and removing component from its owner's
list.

The second pattern, where the temporary local component has an owner,
has many variations where different owners are used. Sometimes
Application is used, sometimes a specific form variable, sometimes even
Self.Owner . Besides the fact that accessing any global variable from the
method represents a bad coding pattern, the fact that you can actually
pass just about any component as an owner proves that this is really an
anti-pattern.

There is also a third solution that does not release the temporary local
component at the end of the method. This is bad code, because it will not
create a memory leak in the classic sense, but this temporary component
will not be released until its owning component is released. Effectively,
calling that method over and over again will continually add new Local
components to the TFooComponent instance's owned components list, wasting
memory and may eventually result in an out-of-memory exception.

This kind of code can be used only if we need to keep the locally
constructed component alive longer than the method's scope.
procedure TFooComponent.DoSomething;
var
Local: TComponent;
begin
Local := TComponent.Create(Self);
// do something with component
end;

If the component is constructed as an owned inner field of another


component, then it can be constructed with that component as its owner,
in which case we don't have to release it explicitly in the destructor.

To demonstrate there is no obvious difference in functionality between


using a nil owner with explicit destruction and using Self as the owner, we
can look at the following example, originally written using the first pattern:
program ComponentField;
uses
System.Classes;

type
TEngine = class(TComponent)
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;

TCar = class(TComponent)
strict protected
FEngine: TEngine;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Drive;
end;

constructor TEngine.Create(AOwner: TComponent);


begin
inherited;
Writeln('Engine created');
end;

destructor TEngine.Destroy;
begin
Writeln('Engine destroyed');
inherited;
end;

constructor TCar.Create(AOwner: TComponent);


begin
inherited;
FEngine := TEngine.Create(nil);
end;

destructor TCar.Destroy;
begin
FEngine.Free;
inherited;
end;

procedure TCar.Drive;
begin
Writeln('Driving');
end;

var
Car: TCar;
begin
ReportMemoryLeaksOnShutdown := true;

Car := TCar.Create(nil);
try
Car.Drive;
finally
Car.Free;
end;
end.

Running the above code produces the following output, proving that the
inner engine object instance is properly created and destroyed:
Engine created
Driving
Engine destroyed

Changing the TCar constructor by passing Self as the owner of the inner
field and completely removing the TCar destructor will produce the same
output:
constructor TCar.Create(AOwner: TComponent);
begin
inherited;
FEngine := TEngine.Create(Self);
end;

Less code - no need to write anything in the destructor - seems like a


good incentive to use this variation at all times. However, there are other
considerations that might have an influence on which one we will actually
use in a particular situation.

We already mentioned the downside of the first approach, the need to


explicitly release in the destructor. It seems that the second approach
does not have any downsides. Actually, it has, but they are not as visible.
Using Self as the owner results in shorter code, but at runtime it is a bit
slower and uses more memory. How much of an impact it can have
depends on how many inner fields there are, and how many instances of
the component itself will be created in a particular timeframe.

When we use Self , our inner field component is referenced not by one,
but by two references. One is our field variable, and the other is the
reference stored in the owned component list. Moreover, since the owned
component list is constructed on an as-needed basis, even a single inner
field will result in the construction of the owned components list that will
consume both additional time and memory. The process of inserting and
removing the component from the owned list also takes time.

The TComponent class is pretty heavy either way, so counting bytes and
nanoseconds might look like premature optimization - but unless you
have a specific reason to use Self as an inner field owner, using nil is still
a good idea.

10.4 TComponent notification system


private
FFreeNotifies: TList<TComponent>;
...
protected
procedure Notification(AComponent: TComponent; Operation: TOperation); virtua
...
public
procedure FreeNotification(AComponent: TComponent);
procedure RemoveFreeNotification(AComponent: TComponent);
...
end;

The TComponent notification system does not represent a shared ownership


model, but rather a weak reference notification. It only prevents you from
accessing stale references. You can freely keep multiple references to a
component, and you will be notified when the component is about to be
destroyed, but those references are not at liberty to release the
component as they please.

Only the component's owner is allowed to release the component, and


besides the owner you can also keep one more dedicated reference that
can release the component before it would be released through its owner,
since the component's owner will be notified that the component is being
destroyed. And that is just about all you can do. Anything more will get
you in trouble.

The notification system is a commonly used pattern when one


component or control uses another as an optional collaborator. Linking a
collaborator component can be done either through the IDE using the
Form Designer, or in code. Of course, linking through the Form Designer
is available only for published properties.

For instance, if we want to use an accelerator key in a label control, we


also need to set the FocusControl property that will receive focus when the
accelerator is pressed.

We can achieve the same through code:


Label1.FocusControl := Edit1;

Label does not own its FocusControl , it is merely a collaborator object that
will be used only if it is set.

If we decide to delete the Edit1 control, Label1.FocusControl would keep


pointing to invalid control. This is where the notification system comes in.
The setter for FocusControl will add Edit1 to its free notification list. When and
if Edit1 gets released, Label1 will receive a notification and will be able to
reset its FocusControl back to nil .

What you must never do is use such an optional property reference to


release a referred component. For that matter, you must never use any
property reference to release a referred object instance, since they are
owned by someone else. Calling Label1.FocusControl.Free is strictly forbidden,
as it would leave Edit1 reference in an invalid state.

Technically, if you created such a collaborator component in code and


you will not keep any other reference to that object, besides its owner,
you could call Free on such a reference as shown in the following
example.

Self represents some component that can take ownership of the TEdit
instance, presumably a form, or we have to make sure that
Label1.FocusControl.Free will always be executed to prevent memory leaks.

Label1.FocusControl := TEdit.Create(Self);
...
Label1.FocusControl.Free;

However, such code, even if technically correct, violates general Delphi


coding patterns and using it is not advisable.

The following example will assist us with exploring the inner workings of
the TComponent notification system. This could also be done by looking at
various VCL or FMX classes that have optional components, but stepping
through that code could be more tedious. Another advantage of a
separate example is providing basic template code for using the
notification system in your own custom components or controls.

Exception handling in the main code block is omitted, since it is not


relevant to the demonstration. All components are created without an
owner, because having an owner is also not relevant here.
program MagicNotifications;

uses
System.Classes;

type
TMaterial = (Wood, Stone, Metal);

TMagicWand = class(TComponent)
public
Material: TMaterial;
procedure Magic;
function MaterialToString: string;
end;

TWizard = class(TComponent)
strict protected
FWand: TMagicWand;
procedure SetWand(const Value: TMagicWand);
procedure Notification(AComponent: TComponent; Operation: TOperation); over
public
procedure UseWand;
published
property Wand: TMagicWand read FWand write SetWand;
end;

procedure TMagicWand.Magic;
begin
case Material of
Wood : Writeln('Casting magic with wooden wand');
Stone : Writeln('Casting magic with stone wand');
Metal : Writeln('Casting magic with metal wand');
end;
end;

function TMagicWand.MaterialToString: string;


begin
case Material of
Wood : Result := 'wood';
Stone : Result := 'stone';
Metal : Result := 'metal';
end;
end;

procedure TWizard.Notification(AComponent: TComponent; Operation: TOperation);


begin
inherited;
if Operation = opRemove then
begin
if AComponent = FWand then
begin
Writeln('Wand has been destroyed: ', FWand.MaterialToString);
FWand := nil;
end;
end;
end;

procedure TWizard.SetWand(const Value: TMagicWand);


begin
if Assigned(FWand) then
begin
Writeln('Dropping wand: ', FWand.MaterialToString);
FWand.RemoveFreeNotification(Self);
end;
FWand := Value;
if Assigned(FWand) then
begin
Writeln('Wand picked up: ', FWand.MaterialToString);
FWand.FreeNotification(Self);
end;
end;

procedure TWizard.UseWand;
begin
if Assigned(FWand) then FWand.Magic
else Writeln('Cannot cast magic without a wand');
end;

var
Wizard: TWizard;
WoodenWand, MetalWand: TMagicWand;

begin
ReportMemoryLeaksOnShutdown := true;

// creating wooden wand


WoodenWand := TMagicWand.Create(nil);
WoodenWand.Material := Wood;

// creating metal wand


MetalWand := TMagicWand.Create(nil);
MetalWand.Material := Metal;

// creating wizard
Wizard := TWizard.Create(nil);

// wizard uses wand before he has one


Wizard.UseWand;

// wizard picks up metal wand and uses it


Wizard.Wand := MetalWand;
Wizard.UseWand;

// wizard picks up wooden wand and uses it


Wizard.Wand := WoodenWand;
Wizard.UseWand;

// fire destroys all wooden wands


WoodenWand.Free;

// wizard tries to use wand but he no longer has one


Wizard.UseWand;

// clean up
MetalWand.Free;
Wizard.Free;
end.
Running the above code will result in the following output.
Cannot cast magic without a wand
Wand picked up: metal
Casting magic with metal wand
Dropping wand: metal
Wand picked up: wood
Casting magic with wooden wand
Wand has been destroyed: wood
Cannot cast magic without a wand

Let's start our code exploration with the UseWand method. It is not related to
the notification system, but to handling optional fields, in this case FWand
and the related property Wand . Since Wand is optional we must check it for
nil before we can safely use it. This is a common pattern with optional
fields or any other kind of optional variables. In the above code, the
UseWand method also has an else branch implemented - if Wand is nil , it prints
the appropriate message - but usually when dealing with optional fields
we would have just implemented the if branch, because there would be
nothing we must do in the else branch.

Implementing a notification system requires several pieces of code. The


first one is overriding the Notification method and the second one is
implementing a setter for an optional field that adds and removes itself
from the optional component notification list.

In the setter, we first remove ourselves from the current FWand free
notification list by calling RemoveFreeNotification(Self) , then we set the new
FWand and add ourselves to that wand's notification list. Of course, since
the wand could be nil , before we call any of the abovementioned
methods we must make sure that FWand is not nil .
procedure TWizard.SetWand(const Value: TMagicWand);
begin
if Assigned(FWand) then FWand.RemoveFreeNotification(Self);
FWand := Value;
if Assigned(FWand) then FWand.FreeNotification(Self);
end;

Overriding the Notification method is a bit more complicated, since this


method can be called for various different components and it is also
called for two distinct events, when AComponent is being inserted or
removed. Insertion will be called when we are setting the owner of the
component. For implementing a free notification mechanism, we are only
interested in removal.

To make sure we are dealing with the right component we also have to
check whether the removed AComponent is the one we are looking for - in
this particular case, FWand . In our example, when the wooden wand is
destroyed in a fire, this piece of code is responsible for removing that
wand from the wizard, preventing access to an invalid reference. That is
why calling UseWand afterwards will not result in a crash or undefined
behavior, but will instead print Cannot cast magic without a wand.

If by any chance the wizard were holding the metal wand while the
wooden wand was being destroyed, the wizard would not be notified of
that destruction.
procedure TWizard.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited;
if Operation = opRemove then
begin
if AComponent = FWand then FWand := nil;
end;
end;

So far, the above Notification method does not look complicated at all. But,
if we extend our TWizard class and add additional optional properties, it will
get longer in no time.
TWizard = class(TComponent)
strict protected
FWand: TMagicWand;
FShield: TShield;
FBag: TBag;
...
end;

procedure TWizard.Notification(AComponent: TComponent; Operation: TOperation);


begin
inherited;
if Operation = opRemove then
begin
if AComponent = FWand then FWand := nil
else
if AComponent = FShield then FShield := nil
else
if AComponent = FBag then FBag := nil;
end;
end;

10.4.1 TComponent notification system weaknesses

While the TComponent notification system flawlessly does what it is meant to


do, and was a great solution at its time, it is a relic of the past. It still
works great, and all Delphi visual frameworks are built on top of it, so you
cannot really get around it, but it is important to realize that nowadays
there are better options for managing weak references.

If you are designing some piece of code from scratch, it would be prudent
not to rely on TComponent notification as a weak reference model, unless you
are extending Delphi visual frameworks or making any kind of design
time system - in such a case you would be bound by the existing design,
where TComponent serves as base class.

The first flaw is that you cannot just take a reference to some component
and be done with it. You have to manually add the appropriate code so
you would be notified when you can no longer use that reference. You
also have to manually add code that will nil that reference. So in order to
have a fully functional notification system there is a whole lot of
boilerplate code you have to write in setters as well as the Notification
method.

With automatic reference counting in place, all that elaborate code could
be replaced with a single [weak] attribute. When the original object
instance is destroyed, all [weak] references pointing to it will be
automatically nilled (zeroed) and we would not have to take care of that
ourselves.
[weak] FWand: TWand;

Using reference counted object instances through interfaces and zeroed


[weak] references is a much safer and easier way to achieve the above. If
you cannot use reference counted instances, or you use older versions of
the classic compiler that do not have [weak] attribute support, there are
weak pattern implementations that can be used in such cases. Of course,
on the ARC compiler all of the above is already given by default.

And the next and more fatal flaw, is that the TComponent notification system
undermines ARC. It is not designed for the ARC compiler and the
combination of the two is not exactly working smoothly. You can read
more about it in ARC chapter, specifically in DisposeOf .

Anyway, if you plan to write any cross-platform code, you should avoid
using TComponent and its notification system as much as possible.
11 Automatic Reference Counting -
Overview
ARC stands for Automatic Reference Counting. It is a form of automatic
memory management system where the usage of each object reference
is tracked - counted - and an object instance is automatically released
when it is no longer reachable through any strong reference (see below) -
in other words, when its reference count falls down to zero.

This chapter, along with the next one, are introductory chapters intended
to give a general overview on ARC concepts and terminology as well as
the origins of ARC in Delphi. A deeper coverage of those concepts,
features and specific behaviors in both classic and NextGen ARC
compilers can be found later on, starting with the ARC - Concepts
chapter.

11.1 Golden Rule of ARC

An object or interface reference can either be nil or point to a valid


object instance.

Conceptually, this is one of the core features of ARC as a memory


management system. Technically, there are exceptions to that rule, but
they have to be taken more as a necessary evil required to solve some
optimization and implementation details, not as a general ARC principle
and programming practice. Those are: the [unsafe] attribute, storing
references as raw pointers, and DisposeOf .

What is the significance of the above rule, how can you count on it if it
can be broken? If it is not actually true all the time, how can it even be
considered a rule, let alone a golden one?
It is not that every reference will be either nil or valid, but that you expect
them to be. What is the difference between expecting valid references in
ARC compared to expecting valid references in manual memory
management? You certainly don't want to use invalid references
anywhere.

The difference is in what is given by default.

In ARC, you start with all references initialized to nil , when you create
your object instances you deal with valid objects until they either go out of
scope or you explicitly nil them. By default, all ARC references follow the
golden rule of ARC. You have to explicitly create possible invalid
references by writing unsafe code.

On the other hand, manual memory management gives you anything by


default, from invalid references to nil ones. After you are finished using
your valid object instances and you release them, by default they turn
into invalid references, unless you explicitly nil each and every reference
to such an object instance. Since references can be invalid by default,
you have to make sure they are either valid or nil every step of the way.

Knowing that you are dealing with valid or nil references, greatly
streamlines the coding process and increases safety. Instead of having
potential invalid references running around, you have them confined in
safe corners where you can more easily keep them under control.

11.2 Strong & weak references


ARC recognizes two types of references: strong and weak. Strong
references are ones that participate in the reference counting mechanism
- they are owning references. On the other hand, weak references are
references that do not participate in reference counting. It is important to
note that even though weak references don't directly participate in the
counting mechanism they can be used to obtain new strong references to
a particular object instance.

All references are strong by default, unless explicitly marked as weak. An


object instance is created with a reference count of 1 and each additional
strong reference to it increases that count. When a strong reference goes
out of scope, is set to nil , or we point it to some other object, the original
instance's reference count is decreased. When the reference count
reaches zero, the object is immediately destroyed and its memory
released. That kind of immediate and predictable object destruction is
one of the core aspects that differentiates ARC from garbage collection.

11.3 Weak references


Delphi has several options for achieving weak references as ARC
concept of references that don't participate in reference counting
mechanism: [weak] or [unsafe] attributes, or storing reference as raw
pointer. Which one should be used depends solely on the requirements
of a particular piece of code. However, using [weak] is preferred over
[unsafe] and [unsafe] over raw pointers. It is important to understand that
they are not fully interchangeable.

Since one of the attributes is called [weak] , to more clearly distinguish


between general concept of weak references, where it is not important
which one is used, and the Delphi attribute, I will be using Delphi attribute
syntax when referring to the particular attribute.

11.3.1 [weak] attribute

The [weak] attribute marks a zeroed weak reference. When the referenced
object is destroyed, zeroed weak references will be automatically set to
nil . This is a very important feature, because it prevents dangling
references and enforces the Golden rule that any reference to an object
instance must be either a valid reference or nil . We can safely use [weak]
as a starting point for marking weak references because we cannot go
wrong if we use the [weak] attribute on any weak reference we can
possibly have.

If the [weak] attribute is safe to use and does not leave dangling
references, why on Earth would we need anything else?

Well, nothing is perfect and there is a teeny-weeny issue with [weak] . In


order to zero that reference when strong references go out of scope, the
application has to track them, and that introduces some runtime
overhead. Using a large number of [weak] references can introduce a
significant performance loss.

11.3.2 [unsafe] attribute

[unsafe] to the rescue (kind of). For references marked with the [unsafe]
attribute, the compiler does not generate any reference counting and
tracking code. But on the other hand, while using [unsafe] , you have to be
wary of dangling references. Because [unsafe] is reference counting
without a safety belt, and as such violates the Golden rule, we have to be
very careful how and where we use it.

11.3.3 Pointer

The third option for disabling the counting mechanism is storing a


reference as a raw pointer - this approach is also the only option in
classic compilers older than Delphi 10.1 Berlin.

Storing a reference as a pointer works the same way as using the [unsafe]
attribute, but with one significant disadvantage - in order to use that
pointer, we have to use typecasting. Where possible, using [unsafe] is
preferred to using pointers.

Raw pointers do have one significant use case, though. All other
references stored in arrays are strong ones. If you need to maintain list of
non-zeroed weak references, an array of pointers is the simplest viable
solution. On the other hand, maintaining list of zeroed weak references
requires more complex solutions - wrapping weak references and storing
wrappers instead direct references in array.

11.3.4 const and [ref] parameters

Parameters marked as const or [ref] will be treated as raw pointers and


will not trigger the reference counting mechanism.

11.3.5 Object references in classic compiler

Since the classic compiler does not have reference counting for object
references, they will act as raw pointers as far as reference counting is
concerned and will not trigger the reference counting mechanism. Only
interface references act as strong ones in the classic compiler, unless
they are not additionally marked as weak.

In the following example we have two references to an object instance,


Foo and Bar , the first a strong one represented by a solid line, and the
other a weak reference represented by a dashed line. Foo participates in
reference counting and Bar doesn't. As long as Foo points to that object, it
will remain allocated and we can also use the weak Bar reference to
access it. However, the moment Foo stops pointing to that object instance
it will be deallocated, and the Bar reference will no longer be usable either.

We can also have several strong references to a single object instance.


In the next example, Foo and Bar are both strong references. That object
instance will remain allocated as long as one of those references points
to it.
11.4 Ownership
ARC is very strict when it comes to ownership and owning references.
Owning references are strong references and they will keep an object
alive. Failure to make the distinction between owning and non-owning
references can result in strong reference cycles and memory leaks.

All owning references must be strong. All other references should be


weak. Every object instance must have at least one strong reference to
keep it alive, and that makes it fairly obvious that any root reference
providing the main access point to any object or object hierarchy must be
a strong one.

An object should never hold a strong reference to its owner, parent or any
other similar reference pointing back to its root.

11.5 Strong reference cycles


One of the problems with reference counting is (strong) reference cycles.
A reference cycle occurs when two object instances hold strong
references to each other. When that happens, those objects' reference
counts will never reach zero and they will never get released even if there
are no other strong references to either of those objects and they can no
longer be reached through code. This is how memory leaks are created
under ARC.

In the following example, the variable FooBar is pointing to some object


instance, representing a strong reference that participates in reference
counting and increasing its reference count by 1. That object instance
contains the field Bar , pointing to another object instance. That second
object instance contains the field Foo which points back to the first object
instance. Both Foo and Bar variables represent strong references and
increase the reference count of the referred objects by 1.

The first object instance's reference count is 2 - there are two strong
references to it, FooBar and Foo . The second object instance's reference
count is 1 - there is only one strong reference pointing to it, Bar . The
whole object hierarchy is reachable through the root FooBar reference.
Now, if we set the FooBar reference to nil , or it goes out of scope, the
reference count of the first object will be decreased. Since there is a
strong reference in the second object pointing to the first one, the
reference count of the second object will remain higher than zero, and
the first object will not be destroyed. Even though both objects are no
longer reachable - the root reference no longer points to the first object,
they will both remain in memory until the application is terminated,
effectively creating a memory leak.

More complex reference cycles can involve many chained objects, as


well as hidden implicit references and captures created by the compiler.
No matter what kind of cycle we are dealing with, recognizing them and
breaking them is the developer's job.

Usually, cycles are solved by converting superfluous strong references


into weak ones, or by explicitly writing code that will break the cycle at a
certain execution point. Which solution we will use depends on the
situation and our requirements, though weak references are the preferred
method.
In previous example we can break the cycle by marking the Foo variable
as weak. When we assign nil to FooBar , there will be no strong references
to the first object and it will be destroyed. During that process, the Bar
variable will be cleared resulting in the destruction of the second object.

Another way of breaking that cycle would be assigning nil to the Bar
variable before assigning nil to the root FooBar .
Bar := nil;
FooBar := nil;

The above sequence will trigger the destruction of the second object,
since we have removed the last strong reference to it. During that
process, the Foo variable holding the second strong reference to the first
object will be cleared, leaving only a single strong reference - FooBar . Once
we assign nil to FooBar , the first object will be destroyed, too. Although that
works, it fits the definition of "clever" code, in that everyone who touches
the module must properly understand why and how it works.
12 Automatic Reference Counting in
Delphi
12.1 ARC in classic compiler - Interfaces
Before taking a deep dive into how reference counting in Delphi actually
works, knowing a bit of history can shed some light on how and why it is
implemented the way it is.

ARC in Delphi is nothing new. Delphi introduced reference counted object


instances with support for interfaces in Delphi 3. Their design has been
influenced by Microsoft's COM - Component Object Model, introduced in
1993, as a platform and language independent application binary
interface for creating distributed, interactable object-oriented
components. Since COM uses ARC for managing objects' lifetimes,
Delphi adopted that model in its interface design.

Interfaces are contracts - they define functionality - declarations of


methods that must be implemented by a class in order to satisfy a
particular interface without assuming and imposing what any particular
implementation may look like. Besides COM interoperability, interfaces
also provide a means of achieving abstraction and loose coupling as
object-oriented programming principles. Since Delphi class can only
inherit from single class, but it can implement many interfaces, using
interfaces opens the door to multiple inheritance.

All COM interfaces must inherit from the base COM interface - IUnknown -
and subsequently all COM object implementations have to implement the
following three methods from IUnknown declaration:

AddRef - Increments the reference count of an object


Release - Decrements the reference count of an object
QueryInterface - Retrieves pointers to the supported interfaces on an
object through GUID - Globally Unique Identifier
In Delphi, all of the above methods are defined in the base interface
IInterface , and IUnknown is just its alias.

While all three methods' implementations are required to satisfy the COM
interface model, only _AddRef and _Release are directly related to the
reference counting mechanism and depending on their implementation in
a class, reference counting on that particular class' object instances can
be either enabled, disabled, or delegated to another object instance.

When dealing with classes that implement interfaces, the compiler will
automatically insert calls to the reference counting methods _AddRef and
_Release at the appropriate places. This makes Delphi interface references
directly tied to memory management.

While explicitly calling _AddRef and _Release is not recommended, as the


prefix _ suggests, it is not prohibited either, and is sometimes used to
solve particular reference counting issues.
type
IInterface = interface
['{00000000-0000-0000-C000-000000000046}']
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;

IUnknown = IInterface;

Interfaces can be declared with or without a GUID. It is used only for


interface identification. If you don't provide GUID with the interface
declaration you will lose the ability to use QueryInterface as mechanism for
testing whether object instance supports queried interface and with it the
ability to use Supports function, as well as the is and as operators.

12.2 Delphi NextGen ARC compiler


Besides the usual drawback of manual memory management - managing
memory is the sole responsibility of the developer and thus poses a
certain burden - the classic Delphi compiler has another important
drawback: interfaces are reference counted. Mixing interface and object
references is prohibited unless the implementing class has reference
counting disabled. This is one of the worst leaky abstractions - you have
to know the implementation details of each class in order to know how to
reference it, and whether or not you have to manually handle its memory.

The ARC compiler brings a solution to the above problem. Reference


counting is no longer tied to a class' implementation details and it is
automatically supported on all references - object and interface. This is
achieved by extending the existing reference counting mechanism to
object references. Unified memory management now allows writing code
without knowing the inner workings of a class. Besides fixing the object -
interface management model, the ARC compiler and its automatic
memory management allowed the implementation of other features, like
class operators.

Sounds fine - in theory. In practice, it replaces one leaky abstraction with


another - namely DisposeOf . Also, some reference counting issues related
to interfaces have been transferred to the ARC compiler where they pose
much greater problem. There is also a variety of missing syntactic sugar
features that would make writing ARC-compliant code easier.

Regardless of the issues - that can and hopefully will be resolved as the
ARC compiler and related frameworks mature, the ARC compiler
constitutes a solid foundation for the future. The main problem is - and
will be in the foreseeable future - not the compiler itself, but code that has
to maintain compatibility with the classic compiler. Under such
circumstances, code has to be written following manual memory
management logic, which only drags the ARC side down and prevents us
from unleashing its full potential.

12.2.1 Existing code in the context of ARC

There are no magic solutions for porting existing code to the ARC
compiler. Depending on the code, porting can be anything from extremely
easy to extremely hard. There are no quick fixes, clever tricks or five-step
guides that can help.

This is not a simple change in syntax port where you have to do some
dull find-replace or name things differently changes and you are done.
This is a deep change in the memory management system, the very
foundation, and this is the kind of change that changes the basic rules of
the game. In order to play, you have to learn the new rules. There is no
easy way around that. And the new rules have been laid out in the ARC
chapters - and, in a more general form, throughout this whole book.

When you know how Automatic Reference Counting functions as a


memory management system, what the rules it follows are, how it
behaves and what are certain patterns you have to pay attention to, you
will know how to change your code. You will be able to recognize the
code that needs to be changed, and you will know how to change them.
That does not mean that changing any particular piece of code will be
easy, because that depends on the code itself.

Only code that already used ARC in the classic compiler in the form of
reference counted classes through interface references, will run as-is in
the ARC compiler. Obviously, that code already follows the rules of the
reference counting mechanism.

The one thing that makes this port harder, is that there is no compiler
guidance, like there was, for instance, in Unicode conversion. However,
this is not due to any deficiency in the compiler, but due to the nature of
this change. Regardless of the size of your codebase and how good or
bad that code is, you will have to go through all the code, line by line,
making changes as you go, and sometimes you will have to make
multiple passes.

12.2.2 Delphi ARC is not Objective-C/Swift ARC

There are a lot of comparisons going on between Delphi's ARC compiler


and Objective-C/Swift's ARC compiler. On the surface, they look quite the
same. Everything covered in ARC Overview applies to both: strong and
weak (including unsafe/unowned) references, the same principles apply
to strong reference cycles, including capturing self in anonymous
methods/closures. And that is about it. All similarities end there. The
second you look under the hood, there are two very different
implementations and it is important to recognize that fact in order to avoid
issues when expected behavior, based on experience with one compiler,
is quite different from actual behavior in another.
One of the common questions asked regarding Delphi's ARC compiler is
"Why can't we have a per-unit or even a global ARC switch? Objective-C
has it, so why shouldn't Delphi?" But, Objective-C didn't exactly
implement manual memory management the way Delphi does. Objective-
C had something called the MRR (Manual-Retain-Release), also referred
to as the MRC (Manual Reference Count) model - basically this is also a
reference counting model, but the developer inserts the reference
counting code manually. Behind the scenes, objects are handled in the
same way under MRR and ARC - the only difference is who writes the
code - the developer or the compiler. That is why Objective-C can have
an ARC switch. It merely says to the compiler - this source code handles
reference counting itself, don't add any on your own, or on the other
hand, this source code does not manually count its references, please
insert reference counting code where appropriate.

Of course, there is still interfacing with fully manually managed


frameworks that needs a different approach - but the point is if you look
at Apple's Objective-C frameworks, counterparts of RTL/VCL/FMX, they
are all reference counted in one way or another. That is why you can
have an ARC switch in Objective-C.

On the other hand, in Delphi the manual memory management model is


completely detached from the ARC model, in the sense that you cannot
mix reference-counted and non-reference-counted object instances. They
are two balloons and you cannot mix the air inside them without popping
them.
13 How the reference counting
mechanism works
The same mechanism that exists for interface references in the classic
Delphi compiler is simply extended to object references and used to
provide automatic reference counting on NextGen ARC compilers -
achieved by adding the __ObjAddRef and __ObjRelease methods to the root
TObject class, along with some other necessary implementation details.

While there are different reference counting methods for interface and
object references, their purpose is the same - increasing and decreasing
the object instance's reference count. To avoid repetition while explaining
the algorithms I will use the xxxAddRef and xxxRelease notation when the same
explanation applies to both variants. Another way to refer to them will be
using the terms increasing and decreasing the reference count.

The general reference counting principles and mechanisms work the


same across all compilers. The only difference is that ARC compilers
have those implemented on all object instances, no matter how they are
referenced. Code and examples will cover both reference counting
implementations. However, code that uses object references is meant to
work solely on ARC compilers.

Specific aspects of the reference counting mechanism that are valid for
classic compilers are covered in the Interfaces chapter.

The definition of reference counting talks about counting strong


references to an object instance - interface or object references. The next
question that arises is what exactly represents a strong reference?
Variables, fields, array items, and parameters are obvious. Anything that
we can declare as storage for either an interface or object reference
constitutes a strong reference - unless explicitly marked as weak. Not so
obvious are hidden implicit references created by the compiler, as they
are always strong. But it is rather easy to forget that besides the
automatic reference counting code inserted by the compiler at
appropriate places while tracking strong references, we can also explicitly
call reference counting methods and interfere with the automatic
reference counting mechanism.

With the spotlight going to the strong (and weak) references, it is easy to
forget that the real actor is the object instance. The reference counting
happens there, and looking at it from the instance's perspective makes it
easier to understand the process. What really matters in the end, is not
how many visible or invisible references we have at any point in time, but
how many times the xxxAddRef and subsequently xxxRelease methods have
been called - and what is the actual value of the RefCount property of the
object. Having that in mind, it is also much easier to understand how we
can influence an object's reference count through weak references, or in
other words; how we can get a strong reference through a weak one.

Another important thing in understanding the reference counting process


is reference scope. Scope represents reference lifetime - detached from
the instance lifetime it points to. Each reference is automatically initialized
to nil when it enters the scope, and is automatically finalized when it exits
the scope - if at that time the reference points to an object instance, it will
call xxxRelease on it.

There are two sides of reference counting that make up the whole. The
first is the instance itself, and the actual count stored within the instance,
and then there are references where changing the state (value) of the
reference triggers the reference counting mechanism in the associated
object instance.

We can watch a particular object instance through its lifetime and


observe changes in all the different references that point to it, and we can
also watch a particular reference through its lifetime (scope) and observe
changes in the different object instances to which it may point.

While each object instance can have multiple references, weak and
strong, pointing to it at any point in time, each reference can only point to
a single object instance at any time - or be nil .
Besides explicit calls to reference counting methods that can be done
either within the object instance itself or through some outside reference,
all other triggers will happen through explicit or implicit strong references.
Knowing what code is responsible for reference state (value) changes
means knowing what kind of code triggers the reference counting
mechanism on the involved object instances and how. Each trigger works
independently of the others, and their impact is combined.

All reference counting code that is automatically inserted by the compiler,


is implemented in the form of RTL helper methods, has the appropriate
nil checks in place, and will not trigger any errors if it encounters a nil
reference. The only code that can trigger an exception if called on nil
reference is code that explicitly calls the xxxAddRef and xxxRelease reference
counting methods.

13.1 Reference counting triggers


Explicit calls to xxxAddRef and xxxRelease
Strong reference initialization and finalization
Assigning nil to a strong reference
Strong reference assignment after construction
Assigning to a strong reference
Passing parameters
Interface casts
Function result
Other implicit references
Anonymous method captures

13.1.1 Explicit calls to xxxAddRef and xxxRelease

Explicit calls to the xxxAddRef and xxxRelease methods - either through any
kind of reference, weak or strong, that points to a non-nil valid object
instance, or within the instance itself - respectively increase or decrease
that instance's reference count.

Explicitly inducing reference counting falls into the Don't do it, unless you
really know what you are doing category. And by knowing what you are
doing, I mean you understand the principles behind the code and why at
that point you have to interfere with the reference counting mechanism.
"It fixes my code", is not a sufficient reason. Having said that, sometimes
you will have to write such code without really knowing why. Reference
counting is an extremely fragile mechanism if tampered with. You may fix
one piece of code, but break another as a result, and that may not be
immediately obvious, but only when you add more unrelated code into
the picture.

Use explicit calls only as a last resort.


var
Intf: IFooInterface;
...
Intf._AddRef;
...
Intf._Release;

var
[weak] Intf: IFooInterface;
...
Intf._AddRef;
...
Intf._Release;

var
[unsafe] Intf: IFooInterface;
...
Intf._AddRef;
...
Intf._Release;

var
Intf: Pointer;
...
IInterface(Intf)._AddRef;
...
IInterface(Intf)._Release;

// TFooObject class supports interfaces


procedure TFooObject.Counting;
begin
_AddRef;
...
_Release;
end;

Explicit calls to reference counting methods on object references have


the same variations as the above examples that work on interface
references. Obj can also be [weak] or [unsafe] , or stored in a raw pointer.
var
Obj: TFooObject;
...
Obj.__ObjAddRef;
...
Obj.__ObjRelease;

procedure TFooObject.Counting;
begin
__ObjAddRef;
...
__ObjRelease;
end;

13.1.2 Strong reference initialization and finalization


All explicit and implicit strong references will be automatically initialized to
nil before they enter their scope and each such initialization will be
matched with a call to the RTL helper methods _IntfClear or _InstClear -
depending on whether the particular reference is an interface reference
or an object reference - at the end of that scope. Since both methods
have a nil check in place before calling the xxxRelease method on the
reference, they can be safely called on nil references. Even if a valid
object instance was never assigned to the reference, or if it has been
explicitly cleared with a nil assignment, those calls will not fail due to the
previously mentioned nil check.

If the strong reference is a local or global variable it will have the


following initialization and finalization sequences. Implicit (hidden)
references will follow the same pattern.
procedure LocalInterface;
var
Intf: IFooInterface;
begin
// empty routine body
end;

procedure LocalObject;
var
Obj: TFooObject;
begin
// empty routine body
end;

The above will be translated by the compiler into


procedure LocalInterface;
var
Intf: IFooInterface;
begin
// initialization - part of the routine prologue
Intf := nil;

// empty routine body

// finalization - part of the routine epilogue

// calls Intf._Release if Intf is not nil at that point


_IntfClear(IInterface(Intf));
end;

and into
procedure LocalObject;
var
Obj: TFooObject;
begin
// initialization - part of the routine prologue
Obj := nil;

// empty routine body

// finalization - part of the routine epilogue

// calls Obj.__ObjRelease if Obj is not nil at that point


_InstClear(TObject(Obj));
end;

depending on whether it's an interface or an object, respectively.

Instance fields representing strong references will also follow a similar


initialization and finalization pattern. They will be initialized to nil in the
InitInstance call during construction, before the constructor chain is
invoked, and the _IntfClear or _InstClear helper methods will be called in the
CleanupInstance lifecycle method.

13.1.3 Assigning nil to a strong reference


Assigning nil to a strong reference also calls the RTL helper methods
_IntfClear or _InstClear . It is used if we have a particular reason for
removing a strong reference to an object instance at any specific point
before the reference itself goes out of scope and is automatically cleared,
otherwise it is not necessary.
var
Intf: IFooInterface;
...
Intf := nil;

var
Obj: TFooObject;
...
Obj := nil;

The above will be translated by compiler into


var
Intf: IFooInterface;
...
// calls Intf._Release if Intf is not nil at that point
_IntfClear(IInterface(Intf));

and into
var
Obj: TFooObject;
...
// calls Obj.__ObjRelease if Obj is not nil at that point
_InstClear(TObject(Obj));

for interfaces and objects, respectively.

13.1.4 Assigning to a strong reference

Constructing an object instance and assigning it to a strong reference (as


well as assigning any reference - weak or strong - to a strong reference)
will call the _IntfCopy or _InstCopy helpers. Compared to the _xxxxClear
methods whose logic boils down to a nil check, their logic is a tad more
complex.

In the first scenario, assignment after construction, the value of the Source
is a pointer to a newly created object instance - at this point its reference
count is zero, and in the second scenario, the value of the Source is the
value of the reference we are assigning from.

In essence, the value of Dest is stored into a temporary pointer, the Source
reference count is incremented through xxxAddRef , the value of the Source
pointer is copied into Dest , and at last, the reference count of the original
Dest reference, now stored in a temporary pointer, is decreased through
xxxRelease .

Of course, just like all other reference counting helper methods, _IntfCopy
and _InstCopy have nil checks implemented all the way through, and if any
reference is nil at any point, calls to the relevant reference counting
methods are simply skipped.

It is important to note that in the case of construction, the constructor


runs first, and only if the constructor successfully creates the object, does
the assignment take place.
var
Dest: IFooInterface;
...
Dest := TFooObject.Create;

var
Dest: TFooObject;
...
Dest := TFooObject.Create;

The above translates to:


var
Dest: IFooInterface;
...
_IntfCopy(IInterface(Dest), TFooObject.Create);

var
Dest: TFooObject;
...
_InstCopy(TObject(Dest), TFooObject.Create);
var
Source, Dest: IFooInterface;
...
Dest := Source;

var
Source, Dest: TFooObject;
...
Dest := Source;

Is translated to:
var
Source, Dest: IFooInterface;
...
_IntfCopy(IInterface(Dest), Source);

var
Source, Dest: TFooObject;
...
_InstCopy(TObject(Dest), Source);

13.1.5 Passing parameters

Reference parameters passed with the value (default) semantic are


strong references, and they trigger the reference counting mechanism in
a procedure's prologue and epilogue. The reference counting mechanism
triggered by parameter passing has nil checks in place, and passing nil
as a parameter is acceptable.

Parameters passed as references - declared as var , const , [ref] , [weak] or


[unsafe] - will not trigger the reference counting mechanism while entering
and exiting the procedure.
While both const and [ref] exhibit the same behavior when used on object
and interface references, const is conventionally used for disabling the
reference counting mechanism on passed parameters.

Regardless of whether the reference counting mechanism was triggered


or not while passing the parameter, the regular reference counting rules
apply once inside the method.

Passing a parameter with the value semantic


procedure InterfaceParameter(Intf: IFooInterface);
begin
end;

procedure ObjectParameter(Obj: TFooObject);


begin
end;

will be translated into:


procedure InterfaceParameter(Intf: IFooInterface);
begin
// increment reference count - part of the routine's prologue

// calls Intf._AddRef if Intf is not nil at that point


_IntfAddRef(Intf);

// empty routine body

// decrement reference count - part of the routine's epilogue

// calls Intf._Release if Intf is not nil at that point


_IntfClear(IInterface(Intf));
end;

procedure ObjectParameter(Obj: TFooObject);


begin
// increment reference count - part of the routine's prologue

// calls Obj.__ObjAddRef if Obj is not nil at that point


_InstAddRef(Obj);

// empty routine body

// decrement reference count - part of the routine's epilogue

// calls Obj.__ObjRelease if Obj is not nil at that point


_InstClear(TObject(Obj));
end;

On the other hand, passing a parameter with a reference semantic (other


than out )
procedure InterfaceParameter(const Intf: IFooInterface);
begin
end;

procedure ObjectParameter(const Obj: TFooObject);


begin
end;

will not cause any reference counting code to be injected into the
prologue and epilogue of the routine.

13.1.6 out and var parameters

You can only pass [weak] or [unsafe] variables to out or var parameters
marked as [weak] or [unsafe] , respectively. Anything else will result in the
following compiler error:

E2033 Types of actual and formal var parameters must be identical

As already mentioned, var parameters don't trigger any reference


counting mechanisms. However, out parameters do, and their value, if
any, will be discarded before being passed to the procedure. They are
meant only for receiving a value, not for passing one.

To clear the value of the reference passed as an out parameter before it


can be reused inside the procedure, _IntfClear or _InstClear will be called on
it before the call is placed, and after that the regular reference counting
rules will apply.

13.1.7 Interface casts

Casting interfaces using the as operator triggers the reference counting


mechanism through the _IntfCast helper method. Casting is done by
calling QueryInterface on Source and if successful, the result will be assigned
to Dest .
var
Source: IFooInterface
Dest: IBarInterface;
...
Dest := Source as Dest;

The above will be translated by the compiler into:


var
Source: IFooInterface
Dest: IBarInterface;
...
_IntfCast(IInterface(Dest), Source, IBarInterfaceGUID)

Casting has similar effects as a plain assignment. The difference here is


that the as operator triggers reference counting on its own and will create
an implicit reference if there is no immediate assignment.
(Intf as IBarInterface).DoBar;

Is basically translated to:


var
Implicit: IBarInterface;
begin
Implicit := Intf as IBarInterface;
Implicit.DoBar;
end;

13.1.8 Function result

Reference counting of functions that return an object or interface


reference have specific reference counting behavior. Result constitutes a
strong reference, and any code within the function body that involves
Result will follow the regular reference counting rules. That implies that the
reference count of Result will be at least one, unless you are returning nil .

If you assign a function result to a local variable, the compiler will


optimize that function call and will not trigger any additional reference
counting. After the call, the local variable reference count will be exactly
the same as the reference count of the Result variable immediately before
the function returned. However, if you assign a function result to any
other non-local variable, like an instance field or a global variable, the
compiler will create an implicit reference around the function Result , and
follow the usual assignment rules, inserting _IntfCopy or _InstCopy with Result
as the Source reference. In that case, the reference count of the returned
object instance after assignment is complete will be incremented by one
until the implicit reference is cleared after leaving its scope - in the
procedure epilogue.
procedure NewFooInterface: IFooInterface;
begin
Result := TFooObject.Create; // RefCount 1
end;

procedure Local;
var
Foo: IFooInterface;
begin
Foo := NewFooInterface; // RefCount 1
end;

function NewFooObject: TFooObject;


begin
Result := TFooObject.Create; // RefCount 1
end;

procedure Local;
var
Foo: TFooObject;
begin
Foo := NewFooObject; // RefCount 1
end;

The above examples with calling the function and assigning its result to a
local variable will not trigger the insertion of any additional reference
counting code, besides the code inserted due to the already described
mechanisms. The first is assigning to a strong reference Result after
object construction, and the second is inside the Local procedure, where
the local variable will be initialized before entering the scope and finalized
after exiting its scope.

If the result of the function is assigned to a non-local variable, there is an


additional hidden reference created in the process.
type
TFooContainer = class(TObject)
Intf: IFooInterface;
Obj: TFooObject
procedure IntfField;
procedure ObjField;
end;

procedure TFooContainer.IntfField;
begin
Intf := NewFooInterface; // RefCount 2
end;

procedure TFooContainer.ObjField;
begin
Obj := NewFooObject; // RefCount 2
end;

The above methods are equivalent to the following code, where each
assignment will then be translated to calls to helper methods according to
the previously explained rules.
procedure TFooContainer.IntfField;
var
Implicit: IFooInterface;
begin
// method prologue, initialize implicit variable
Implicit := nil;

Implicit := NewFooInterface; // RefCount 1


Intf := Implicit; // RefCount 2

// method epilogue, clear implicit variable


Implicit := nil; // RefCount 1
end;

procedure TFooContainer.ObjField;
var
Implicit: TFooObject;
begin
// method prologue, initialize implicit variable
Implicit := nil;

Implicit := NewFooObject; // RefCount 1


Obj := Implicit; // RefCount 2

// method epilogue, clear implicit variable


Implicit := nil; // RefCount 1
end;
Usually we don't need to concern ourselves with all the intermediate
references created during some process. However, knowing they exist
can help us in some particular situations where we must release some
instance at a particular time and without violating ARC.

For example, if you have related object instances where a particular


order of release must be maintained, implicit references can interfere with
that order and cause unwanted effects. While you cannot influence
implicit references directly, you can convert them to explicit ones and
then you can control their lifetime.

Basically, you just have to imitate what the compiler does behind the
scenes, but with explicitly declared variables. Since explicit variables are
under your control, you can explicitly nil them before the end of the
procedure call, at any point you need.
procedure TFooContainer.IntfField;
var
Explicit: IFooInterface;
begin
Explicit := NewFooInterface; // RefCount 1
Intf := Explicit; // RefCount 2

Explicit := nil; // RefCount 1

// do something with Intf


end;

13.1.9 Other implicit references


Spotting implicit references can be difficult, as they might appear in
unexpected places. On the bright side, their behavior is no different than
the behavior of explicit references in the same circumstances. There are
no additional rules which they follow, and if you bump into an unexpected
implicit reference, taking control consists of either using an additional
variable to avoid implicit reference creation, or minimizing its scope by
moving the code that creates it into another procedure or method.

Loops can also be a source of implicit references. What matters is that


some are confined within the loop scope and are cleared with each
iteration, and some leak to the outside code and have procedure scope.
The latter ones can be problematic if object instance they point to must
be released before the procedure epilogue.

Actually, it is not about loops but about inline functions. However, loops
are a rather common place for hidden function calls. For instance,
accessing collection items in a loop. We don't usually think about it, but
collection items are backed by a getter function. If that function is
declared inline, then an implicit reference for storing the immediate result
of that function will be inserted inline with a smaller scope than that of the
procedure. If the getter function is not inlined, then the implicit reference
will have the same scope as the procedure where the code is located.
And that creates a loop effect where sometimes implicit references leak
and sometimes they do not.

Directly accessing collection items will create an implicit reference whose


scope will depend on whether or not the getter is inlined.
for i := 0 to List.Count-1 do
begin
List[i].Foo;
end;

A small example of the above behavior is also visible if we compare


TList<IInterface> with TInterfaceList . In the former, the getter is inlined, but in
the latter it is not. What is seemingly confusing here is that we already
have an explicit reference where we store the current item first, but an
interface cast requires an additional reference.
uses
System.Classes;

type
IFoo = interface
['{DBBB81DA-1B1B-443C-B9DF-403C4A360E97}']
procedure Foo;
end;

TFoo = class(TInterfacedObject, IFoo)


public
destructor Destroy; override;
procedure Foo;
end;

procedure TFoo.Foo;
begin
Writeln('Foo');
end;

destructor TFoo.Destroy;
begin
Writeln('Foo destroyed');
inherited;
end;

procedure TestList;
var
i: integer;
List: IInterfaceList;
Intf: IFoo;
begin
List := TInterfaceList.Create;
Intf := TFoo.Create;
List.Add(Intf);
Intf := TFoo.Create;
List.Add(Intf);
Intf := TFoo.Create;
List.Add(Intf);

for i := 0 to List.Count-1 do
begin
Intf := List[i] as IFoo;
Intf.Foo;
end;

// Clear all explicit variables


Intf := nil;
List.Clear;
List := nil;
Writeln('end');
end;

begin
TestList;
end.

Executing the above code will result in the following output:


Foo
Foo
Foo
Foo destroyed
Foo destroyed
end
Foo destroyed

The last Foo destroyed is written after end even though we have explicitly
cleared all references we possibly could and that is an indicator that there
is a hidden reference created behind the scenes to cater for the Intf :=
List[i] as IFoo assignment with an interface cast. However, replacing
TInterfaceList in the above example with TList<IInterface> will a generate
different, expected output.
Foo
Foo
Foo
Foo destroyed
Foo destroyed
Foo destroyed
end

Also, removing the cast to IFoo and changing the loop in the version using
TInterfaceList will not require implicit reference to cater for the cast.

Intf: IInterface;
...
for i := 0 to List.Count-1 do
begin
Intf := List[i];
end;

Foo destroyed
Foo destroyed
Foo destroyed
end

Intf: IInterface;
...
for i := 0 to List.Count-1 do
begin
Intf := List[i];
TFoo(Intf).Foo;
end;

We can even cast the interface to TFoo and call the Foo method without
triggering additional counting.
Intf: IInterface;
...
for i := 0 to List.Count-1 do
begin
Intf := List[i];
TFoo(Intf).Foo;
end;

Foo
Foo
Foo
Foo destroyed
Foo destroyed
Foo destroyed
end

To focus on how inline functions behave, we can try the following


example. The Item property has a getter that is not inlined.
type
// IFoo and TFoo declaration
...

TContainer = class(TObject)
strict private
FItem: IInterface;
function GetItem: IInterface;
public
procedure Init;
procedure Clear;
property Item: IInterface read GetItem;
end;

procedure TContainer.Init;
begin
FItem := TFoo.Create;
end;

procedure TContainer.Clear;
begin
FItem := nil;
end;

function TContainer.GetItem: IInterface;


begin
Result := FItem;
end;

procedure TestContainer;
var
Container: TContainer;
Intf: IFoo;
begin
Container := TContainer.Create;
Container.Init;

Intf := Container.Item as IFoo;


Intf.Foo;

Intf := nil;

Container.Clear;
Container.Free;
Writeln('end');
end;

begin
TestContainer;
end.

And the output will show we are dealing with an implicit reference in the
procedure scope:
Foo
end
Foo destroyed

Adding the inline directive to the GetItem function will also create an implicit
reference, but with a reduced scope.
function GetItem: IInterface; inline;

And the order of destruction is changed:


Foo
Foo destroyed
end

It is important to understand this behavior, because the inline directive is


treated as a suggestion for the compiler, and depending on the
circumstances, the compiler can inline a particular procedure or not,
subsequently having an unexpected influence on behavior of our code.

Where order of destruction matters, you should never rely on inline


directive magic and always use an additional explicit reference to do the
appropriate interface casting.
13.1.10 Anonymous method captures

Understanding the variable capture of anonymous methods and its


influence on the reference counting of captured references, along with
how it can create strong reference cycles, is tightly coupled with their
implementation details and therefore covered in a separate Anonymous
methods chapter.

13.2 Tracking [weak] references


A number of RTL helper methods handle the tracking and zeroing of
[weak] references, but knowing exactly how that system works behind the
scenes has very little relevance to the everyday coding process, since
they don't have any influence on object reference counts. The only
influence they have is performance, since tracking and zeroing [weak]
references happens at runtime.

It has already been said that weak references of any kind can be used to
directly call reference counting methods, as well as assigning any kind of
weak reference to a strong one increments the reference count of that
particular instance.

What has not been covered is assigning to a [weak] reference. This


process is rather simple. If a reference already points to some object, its
value will be removed from the weak references tracking list and a new
value - reference will be added to the tracking list.

When an object instance is released, all [weak] references in the tracking


list that point to that particular object instance will be zeroed.

This is a rather simplified explanation of how [weak] references are


tracked, but as I said, the implementation details of that process are not
as important and are subject to change.

13.2.1 Weak reference assignment after construction

There is one aspect of the [weak] reference tracking process you must be
aware of. Assigning to a [weak] reference immediately after construction,
before there are no other strong references to the object instance, will
instantly kill the object, regardless of whether you are using the classic
compiler and interfaces, or the ARC compiler and objects or interfaces.

Assigning to a [weak] reference will trigger the reference counting


mechanism by temporarily increasing and then decreasing instance
reference count in order to complete the process of assigning and adding
the [weak] reference tracking code. Since the object instance is created
with a reference count of zero, that process will increase the count to
one, and then when completed, will decrease it back to zero, resulting in
instant death for our newly created object instance.
var
[weak] Ref: IInterface;

begin
Ref := TInterfacedObject.Create;
Writeln(Assigned(Ref));
end.

=> FALSE

var
[weak] Ref: TObject;

begin
Ref := TObject.Create;
Writeln(Assigned(Ref));
end.

=> FALSE
14 Forming of a reference cycle
One of the simplest types of reference cycles is a parent-child
relationship.

It can be reproduced with the following example for ARC compilers:


type
TChild = class;

TParent = class(TObject)
public
var Child: TChild;
constructor Create;
destructor Destroy; override;
end;

TChild = class(TObject)
public
var Parent: TParent;
constructor Create(AParent: TParent);
destructor Destroy; override;
end;

constructor TParent.Create;
begin
Writeln('Parent constructor');
inherited;
Child := TChild.Create(Self);
end;

destructor TParent.Destroy;
begin
Child.Free;
inherited;
Writeln('Parent destructor');
end;

constructor TChild.Create(AParent: TParent);


begin
Writeln('Child constructor');
inherited Create;
Parent := AParent;
end;

destructor TChild.Destroy;
begin
inherited;
Writeln('Child destructor');
end;

procedure Test;
var
Root: TParent;
begin
Root := TParent.Create;
Root.Free;
end;

begin
Test;
end.

In the above code, we have a TParent object instance holding a strong


reference to a TChild object instance, but the TChild instance also holds a
strong reference to its parent.

This kind of code works well under manual memory management. If we


run the above example using the classic Delphi compiler, we will get the
following output, showing that all object instances were successfully
constructed and destructed when we called Free on the Root reference:
Parent constructor
Child constructor
Child destructor
Parent destructor

However, if we run that code under the ARC compiler, we will get the
following output showing that object instances were constructed, but
never released. We leaked two object instances, one TParent and one
TChild . Each time we call the Test procedure in our code, that procedure
will leak two object instances:
Parent constructor
Child constructor

A slightly modified version using reference counted interfaced objects,


exhibits the same issue (and output) with the classic Delphi compiler.
type
IParent = interface
end;

IChild = interface
end;

TParent = class(TInterfacedObject, IParent)


public
var Child: IChild;
constructor Create;
destructor Destroy; override;
end;

TChild = class(TInterfacedObject, IChild)


public
var Parent: IParent;
constructor Create(const AParent: IParent);
destructor Destroy; override;
end;

constructor TParent.Create;
begin
Writeln('Parent constructor');
inherited;
Child := TChild.Create(Self);
end;

destructor TParent.Destroy;
begin
inherited;
Writeln('Parent destructor');
end;

constructor TChild.Create(const AParent: IParent);


begin
Writeln('Child constructor');
inherited Create;
Parent := AParent;
end;

destructor TChild.Destroy;
begin
inherited;
Writeln('Child destructor');
end;

procedure Test;
var
Root: IParent;
begin
Root := TParent.Create;
Root := nil;
end;

begin
Test;
end.

When we encounter such a cycle in our code, we can break the cycle by
making one of the strong references weak. The next logical question is,
which one of the offending references should be marked as weak?

This is where the concept of ownership comes in. Owning references in


ARC should always be strong. All other references should be weak. An
object should never hold a strong reference to its owner, parent or any
other similar reference pointing back to its root. It also should never hold
strong reference to any sibling object instance.

In the above parent-child relationship, the Root reference is clearly the


owning reference - it provides an access point to the object hierarchy. It is
also clear that the parent owns the child reference and not vice-versa.
Therefore, the weak reference should be the reference to the child's
parent.

While following ownership rules will give us clear picture about strong -
weak references in our object hierarchy, if we don't have a clear idea
about ownership there is an additional approach to solving this issue and
recognizing owning references. We can find owning references by simply
starting at root reference in some hierarchy and following all contained
references in a linear path. If any of the references points back to some
previous or any sibling reference in the hierarchy it will make a cycle, and
therefore such a reference should be weak.

Once we have determined which references are strong and which ones
are weak, we have to decide on a mechanism to use. There are three
ways to prevent reference counting on references: using the [weak] or
[unsafe] attributes, or storing the reference in a pointer.

By adding [weak] in the Parent field declaration from the above example we
solved the cycle:
TChild = class(TObject)
public
[weak] var Parent: TParent;

Or in the interface-based code:


TChild = class(TInterfacedObject, IChild)
public
[weak] var Parent: IParent;

And running the corrected examples will give us the following output,
indicating all objects were successfully constructed and destructed:
Parent constructor
Child constructor
Parent destructor
Child destructor

The classic compiler can have another variation of a parent-child cycle,


where only the parent is a reference counted object instance and the
child is a regular one. Since the child is a regular object, we have to
explicitly release it in the parent's destructor. This will slightly change our
output because the child's destructor will be called before the parent's
destructor finishes its execution.
type
IParent = interface
end;

TChild = class;

TParent = class(TInterfacedObject, IParent)


public
var Child: TChild;
constructor Create;
destructor Destroy; override;
end;

TChild = class(TObject)
public
[weak] var Parent: IParent;
constructor Create(const AParent: IParent);
destructor Destroy; override;
end;

constructor TParent.Create;
begin
Writeln('Parent constructor');
inherited;
Child := TChild.Create(Self);
end;

destructor TParent.Destroy;
begin
Child.Free;
inherited;
Writeln('Parent destructor');
end;

constructor TChild.Create(const AParent: IParent);


begin
Writeln('Child constructor');
inherited Create;
Parent := AParent;
end;

destructor TChild.Destroy;
begin
inherited;
Writeln('Child destructor');
end;
procedure Test;
var
Root: IParent;
begin
Root := TParent.Create;
Root := nil;
end;

begin
Test;
end.

Parent constructor
Child constructor
Child destructor
Parent destructor

In the above examples, the child's destructor runs before or after its
parent's, depending on the code in the destructor. Generally, the order of
their execution does not matter. However, if the child has to access its
parent during the destruction process for any reason, then the child must
be explicitly destroyed within the parent destructor, either by calling Free
or setting its reference to nil , depending on whether we are dealing with
object or interface references.

14.1 When is it safe to use [unsafe]?


Using [unsafe] or pointers for weak references is perfectly safe, if the
lifetime of such a reference is smaller than or equal to the lifetime of the
object instance it points to. If you cannot guarantee that condition, you
should never use the [unsafe] attribute or a raw pointer. If you did use
them, you would need some other notification mechanism to make sure
you know when an unsafe reference no longer points to a valid object
instance.

In the context of our parent-child example, it would be acceptable to mark


Parent as [unsafe] if the child could never outlive its parent. Our example
destroys the child when the parent is destroyed, so as long as we don't
grab and store any other strong reference to the child - where such a
reference could live longer than the parent - using unsafe would be
perfectly acceptable.
15 DisposeOf - ARC with a twist
15.1 DisposeOf in the context of ARC memory
management
In ARC, an object instance is automatically destroyed when its reference
count drops to zero. At that point, the destructor is called and the
instance enters the destruction stage. DisposeOf allows us to circumvent the
reference counting mechanism and trigger object destruction at an
arbitrary point, even if there are more strong references to the object
instance.

breaks ARC. It violates the golden rule of ARC, Any object


DisposeOf
reference can either be nil or point to a valid object instance, and
introduces a third state - a disposed zombie object reference.

Anyone trying to understand ARC memory management should look at


DisposeOf like an addition, a necessary evil, that just solves Delphi-specific
framework issues, and not a concept that really belongs in ARC as the
memory management system itself.

15.2 DisposeOf and object instance lifecycle


We already know that destruction of an object instance has the following
stages: 1. executing BeforeDestruction 2. executing a chain of destructors 3.
executing CleanupInstance to clean up and finalize managed-type fields -
strings, dynamic arrays, variants, interfaces... 4. memory deallocation -
_FreeMem

If the destruction of an object instance is triggered by the reference


counting mechanism, all destruction stages will execute at once.
However, if destruction is triggered by DisposeOf , the first two stages will
execute immediately, and the next two stages - cleaning up the instance
and deallocating its memory - will be executed at a later time following
the regular reference counting mechanism pattern - or in other words,
when the reference count of the instance falls down to zero.
Calling a destructor on an object instance basically renders that object
useless. Since it is neither a live, valid object instance nor is it a nil
(dead) one yet - it will be in a zombie or disposed state, until it finally
dies.
15.3 Disposed property

If you ever feel the desire to check Disposed on a Delphi object


instance, then go and splash yourself with ice cold water until you get
over it.

To recognize that an object instance is no longer usable, the Disposed


property of that instance will be set to True when the destruction process
starts. The Disposed flag is used internally to prevent redundant calls to
BeforeDestruction and destructor chains. Beyond that, it should be used only
for temporary debugging purposes and it really has no place in
production code.

is part of the mechanism that bridges the gap between ARC and
Disposed
manual memory management.

If you look at the ARC side and its coding patterns - it certainly does not
recognize nor have the need for such a property. DisposeOf along with
Disposed has no purpose in pure ARC.

serves a purpose for code written for manual memory


DisposeOf
management and relies on the destructor being called at a particular
point. But if you look at manual memory management and its patterns,
once you are done with an object instance and you trigger its destruction,
you should never access that object instance again. Accessing Disposed is
in direct violation of that rule.

Disposed does not belong to ARC and it does not belong to manual
memory management. Any code using it, beyond the above mentioned
internal purpose, would be neither ARC compliant nor manual memory
management compliant. It would be bad code no matter how you look at
it. If some piece of code needs to check the Disposed property, that means
it is seriously broken by design and has to be fixed properly, by changing
the broken design.
15.4 Why does DisposeOf exist in Delphi ARC
compilers?
The TComponent class was designed with manual memory management in
mind, and this is true for all of its descendants. It uses a notification
mechanism that is not compatible with ARC memory management
because it relies on breaking strong reference cycles in the destructor.
Since TComponent is one of the core classes Delphi frameworks rely upon, it
must be able to function properly under ARC memory management,
while maintaining compatibility with the classic compiler.

Besides the Free Notification mechanism, any other similar framework


designs that rely on explicit cycle-breaking in the destructor will either
need to use DisposeOf or would need to be completely redesigned to be
suitable for ARC memory management.

The DisposeOf method enables the direct calling of object destructors, and
enables such legacy code to play along with ARC.

One thing must be noted here. Any code that uses or inherits from
TComponent or any other class that requires DisposeOf automatically becomes
legacy code in the context of proper ARC management, even if you write
it today. Basically, as long as the internal design of TComponent stays the
same, and as long as the core frameworks continue to use it, all code
that must interact with those frameworks will have to operate within the
constraints the TComponent notification system imposes within the ARC
environment.

A quote from Allen Bauer - Embarcadero Chief Scientist at the time -


Give in to the ARC side:

So what else does DisposeOf solve? It is very common among


various Delphi frameworks (VCL and FireMonkey included), to place
active notification or list management code within the constructor and
destructor of a class. The Owner/Owned model of TComponent is a
key example of such a design. In this case, the existing component
framework design relies on many activities other than simple
“resource management” to happen in the destructor.

TComponent.Notification() is a key example of such a thing. In this


case, the proper way to “dispose” a component, is to use DisposeOf.
A TComponent derivative isn’t usually a transient instance, rather it is
a longer-lived object which is also surrounded by a whole system of
other component instances that make up things such as forms, frames
and datamodules. In this instance, use DisposeOf is appropriate.

15.5 TComponent notification system and ARC


's Free Notification mechanism notifies registered components that
TComponent
a particular component instance is being freed. Notified components can
handle that notification inside a virtual Notification method, and make sure
that they clear all references they may have to the component being
destroyed.

Under non-ARC compilers, that mechanism ensures that you don't end
up with dangling pointers pointing to invalid - released - objects, and
under ARC compilers, clearing references to the destroying component
will decrease its reference count and break strong reference cycles.

However, the Free Notification mechanism is triggered in TComponent 's


destructor, and without a call to DisposeOf and direct execution of their
destructors, two components could hold strong references to each other,
keeping themselves alive during the application's whole lifetime.

, a TList containing a list of components interested in


FFreeNotifies
notifications, is declared as FFreeNotifies: TList<TComponent> and will store a
strong reference to any registered component.

So, for instance, if you have a TEdit and a TPopupMenu on your form and
assign that popup menu to the edit's PopupMenu property, the edit will hold a
strong reference to the popup menu in its FEditPopupMenu field, and the
popup menu will hold a strong reference to the edit in its FFreeNotifies list. If
you want to release either of those two components, you have to call
DisposeOf on them or they will just continue to exist.
While you can try to track those connections manually and break strong
reference cycles before you release any of those objects, that may not be
so easy to do in practice.

The following code will basically leak both components under ARC,
because they will hold strong references to each other, and after the
procedure is finished you will no longer have any external references that
point to either one of those components. However, if you replace Menu.Free
with Menu.DisposeOf you will trigger the Free Notification mechanism and break
the strong reference cycle.
procedure ComponentLeak;
var
Edit: TEdit;
Menu: TPopupMenu;
begin
Edit := TEdit.Create(nil);
Menu := TPopupMenu.Create(nil);

// creating strong reference cycle


Edit.PopupMenu := Menu;

// Menu will not be released because Edit holds strong reference to it
Menu.Free;

// Edit will not be released because Menu holds strong reference to it
Edit.Free;
end;

There are two rules that should be followed when releasing any TComponent
descended object under Delphi ARC compilers:

Using DisposeOf is mandatory regardless of whether the object has an


owner or not
In destructors or in cases where the reference is not going out of scope
shortly after DisposeOf is called, the object reference should also be set to
nil as I will explain more thoroughly in Pitfalls

While in many cases the code will function properly even if the above
rules are not followed, such code would be rather fragile and could easily
be broken by other code introduced in seemingly unrelated places.

15.6 Pitfalls of DisposeOf


Besides breaking ARC, which is bad on its own, because when you
break it you don't have much use of it, there are also two major issues
with how DisposeOf is implemented that developers should be aware of.

15.6.1 DisposeOf does not decrease reference count on the calling


reference

Original QC report 126241, QP report RSP-14681

In other words, calling DisposeOf will not nil the calling reference, and as a
result, its reference count will not be decreased.
type
TFoo = class(TObject)
public
// Log is here for logging purposes only
class var Log: TStrings;
class constructor ClassCreate;
class destructor ClassDestroy;
public
destructor Destroy; override;
procedure FreeInstance; override;
end;

class constructor TFoo.ClassCreate;


begin
Log := TStringList.Create;
end;

class destructor TFoo.ClassDestroy;


begin
Log.Free;
end;

destructor TFoo.Destroy;
begin
Log.Add('Foo Destroy');
inherited;
end;

procedure TFoo.FreeInstance;
begin
Log.Add('Foo Free Instance');
inherited;
end;
procedure DoDispose;
var
Foo: TFoo;
begin
TFoo.Log.Add('');
Foo := TFoo.Create;
Foo.DisposeOf;
// Foo := nil;
TFoo.Log.Add('Foo DisposeOf finished');
TFoo.Log.Add('Foo Assigned ' + BoolToStr(Assigned(Foo), true));
end;

procedure DoFree;
var
Foo: TFoo;
begin
TFoo.Log.Add('');
Foo := TFoo.Create;
Foo.Free;
TFoo.Log.Add('Foo Free finished');
TFoo.Log.Add('Foo Assigned ' + BoolToStr(Assigned(Foo), true));
end;

procedure TestFoo;
begin
DoDispose;
DoFree;
end;

The above code will produce the following Log in the classic compiler.
Since the classic compiler leaves a dangling pointer after release, Assigned
will always produce True , unless we explicitly nil the reference. That is
expected output on classic compilers, where we don't expect nilling of the
reference.
Foo Destroy
Foo Free Instance
Foo DisposeOf finished
Foo Assigned True

Foo Destroy
Foo Free Instance
Foo Free finished
Foo Assigned True

The following Log will be produced in the ARC compiler:


Foo Destroy
Foo DisposeOf finished
Foo Assigned True
Foo Free Instance

Foo Destroy
Foo Free Instance
Foo Free finished
Foo Assigned False

As we can see, Free nils the calling reference on the ARC compiler, but
DisposeOf does not - after calling it Assigned(Foo) is True . If we explicitly nil the
Foo after calling DisposeOf , Assigned(Foo) will show False .

This is proof that DisposeOf does not nil the calling reference, but why
would we care? After all, neither Free nor DisposeOf don't nil the reference in
the classic compiler.

Well, in the classic compiler, Free and DisposeOf are not two different things
but one - Free . They are completely interchangable there. DisposeOf is here
to cater for the needs of particular code that requires executing the
destructor at a specific point even if there are other strong references to
the object instance. And DisposeOf does that, so what is the problem?

The problem is that in the classic compiler, calling Free (or DisposeOf ) will
completely destroy the object instance and release its memory. Unless
you are reusing the object reference, there is no need to reset it to nil .
The ARC side can completely release all instance memory only after
reference counting completes its job and when the reference count of the
object instance reaches zero. Not sooner. You can destroy all you want,
but the reference counting mechanism has to complete its job. And this is
the purpose of zombie objects. They are a thin shell kept alive just to give
the reference counting mechanism something it can work on.

In ARC, object instances can be fully released only after all strong
references to that instance are gone. How do you do that? Besides
simply waiting for them to go out of scope, you can also set those
references to nil to decrement the reference count and get your object
one step closer to the release point. So when you want to get rid of some
object so much that you actually call its destructor, you definitely would
not want to nil the reference you used in that process - because you are
a fan of The Walking Dead. Of course, you want that reference cleaned
up sooner rather than later. There is absolutely no reason in this
Universe, or any other, why DisposeOf should not nil the calling reference. If
the compiler is smart enough to treat Free on ARC as a nil assignment,
surely it could be smart enough to insert a nil assignment after you call
DisposeOf - it has all the information it needs to perform that operation.

In the above example code, our reference is eventually cleared and the
object instance fully released. Yes, that is true, but only because in that
simple example the object reference went out of scope almost
immediately.

Still, that does not deserve so much ranting, doesn't it? Well, you know
that thin shell I was talking about previously? It is not so thin after all.

Oh, and another small thingy. An additional side effect of not nilling the
reference is that any [weak] references are not cleared after the object is
disposed. They will also have to wait for completion of the reference
counting cycle.

15.6.2 DisposeOf does not clean up an instance's inner managed-


type references

Original QC report 126243, QP report RSP-14682

And this is actually a rather severe issue. Instead of thin zombies running
around you can have nice fat ones, too. And while you can explicitly nil
the instance after calling DisposeOf or use some custom made DisposeAndNil
function, working around this one is anything but easy, bordering on the
impossible.

The issue here is that DisposeOf will only call the destructor chain, but the
CleanupInstance method responsible for cleaning up inner managed fields -
strings, objects, interfaces, dynamic arrays... - will be called only after the
reference count of the object instance reaches zero.
type
TFoo = class(TObject)
public
Text: string;
Data: array of byte;
Obj: TObject;
end;

var
Foo, FooRef: TFoo;

procedure DoSomething;
var
Log: string;
begin
Foo := TFoo.Create;
Foo.Text := 'Alive and kicking';
SetLength(Foo.Data, 1024);
Foo.Data[0] := 100;
Foo.Obj := TObject.Create;

FooRef := Foo;

Foo.DisposeOf;
Foo := nil;

Log := FooRef.Text + ' ' + IntToStr(Length(FooRef.Data)) + ' ' +


IntToStr(FooRef.Data[0]) + ' ' + IntToStr(FooRef.Obj.RefCount);

// Log content:
// Alive and kicking 1024 100 1
end;

As a workaround, you have to clean up each and every field manually,


including strings:
destructor TFoo.Destroy;
begin
Text := '';
Data := nil;
Obj := nil;
inherited;
end;

Or you can use FinalizeRecord to clean them all up at once, but only if you
don't have any [weak] fields inside your class or its ancestors or
descendant classes, since this code will walk through the whole
inheritance chain.
destructor TFoo.Destroy;
var
ClassPtr: TClass;
InitTable: Pointer;
begin
ClassPtr := ClassType;
repeat
InitTable := PPointer(PByte(ClassPtr) + vmtInitTable)^;
if InitTable <> nil then
FinalizeRecord(Self, InitTable);
ClassPtr := ClassPtr.ClassParent;
until ClassPtr = nil;
inherited;
end;

If you want to confine FinalizeRecord to the current class you will have to
use the following code and explicitly write for that particular class:
destructor TFoo.Destroy;
begin
FinalizeRecord(Self, PPointer(PByte(TFoo) + vmtInitTable)^);
inherited;
end;

The combined effect of the above two issues can manifest itself in
different ways. From keeping more memory allocated than necessary, to
creating elusive bugs that are caused by an unexpectedly inaccurate
reference count of contained non-owned object and interface references.

Since DisposeOf does not decrease the reference count of the calling
reference, it is important to nil such references in destructors, otherwise
whole object hierarchies can stay alive much longer than needed and in
some cases even during the whole application lifetime.

15.6.3 DisposeOf cannot be used to resolve arbitrary (all) circular


references

The last, but not the least issue with DisposeOf is that it will break circular
references only if there is code in the destructor that explicitly resolves
them - like TComponent 's notification system does.

Reference cycles that are not handled by the destructor should be broken
using the [weak] or [unsafe] attributes on one of the references. That is also
the preferred ARC practice.
DisposeOf should not be used as a quick fix for breaking all reference cycles
(the ones it was never designed for) because it will not work and abusing
it can result in elusive memory leaks.

A simple example of a cycle that will not be broken by DisposeOf is:


type
TChild = class;

TParent = class(TObject)
public
var Child: TChild;
end;

TChild = class(TObject)
public
var Parent: TParent;
constructor Create(AParent: TParent);
end;

constructor TChild.Create(AParent: TParent);


begin
inherited Create;
Parent := AParent;
end;

var
p: TParent;
begin
p := TParent.Create;
p.Child := TChild.Create(p);
p.DisposeOf;
p := nil;
end;

The above code will leak both child and parent object instances.
Combined with the fact that DisposeOf does not clear inner managed types
(including strings), such leaks can be huge depending on what kind of
data you are storing inside. The only (proper) way to break that cycle is
by changing TChild 's class declaration:
TChild = class(TObject)
public
[weak] var Parent: TParent;
constructor Create(AParent: TParent);
end;
16 Interfaces in the classic compiler

Under the magnifying glass all abstractions are leaky.

Interfaces in Delphi are a leaky abstraction if there ever was one.

What is a leaky abstraction?

Leaky abstraction is when some inner implementation detail that should


be encapsulated - hidden - is put out in the open. Instead of completely
abstracting away its inner complexity and leaving a clean, well-defined
interaction interface - it leaks complexity and if and when the inner
implementation changes, it implies changes to outside consumers - and
that is something that should not happen.

This is exactly what happens with interfaces and the fact that they are
entangled with memory management in the classic Delphi compiler. It is a
bit ironic that interfaces, as a tool for achieving better abstraction and
separation of concerns, actually have such a flaw.

Nevertheless, that does not mean they cannot be used for that purpose,
just that you have to think a bit more when using them. This is also more
of a problem for developers coming to Delphi from other languages,
where using interfaces does not involve special memory management
concerns. With reference counting disabled, there is almost no difference
between using non-interfaced object instances and interfaced ones. And
with reference counting enabled, you will get automatic memory
management, which allows you to do some neat things too.

While the ARC compiler solves the problems with interface-object


reference interoperability, there are some interface related compiler
issues, bugs that exist in both compiler flavors and will be clearly marked
as such.
16.1 Mixing interface and object references

In Delphi, using a reference counted class through an object reference


opens the doors of hell.

In plain words, if a class implements reference counting, when you


construct object instances of such a class you must store them in an
interface reference and let the reference counting mechanism release the
instance automatically when the last strong reference goes out of scope.

If a class has reference counting disabled, you can use object references
to store such object instances and you can also safely pass them to
procedures that expect a particular interface supported by that class.
Since reference counting is disabled, such object instances have to be
released manually.

Object references in the classic compiler behave like unsafe references,


no reference counting code is generated for them. We already know that
the reference counted object instance is actually created with an initial
reference count of zero. It is the immediate assignment to the strong
explicit or implicit reference that increases that reference count to one
and keeps the object instance alive. But what happens when we assign
that object instance to the unsafe object reference? Since there is no
assignment to strong reference, the reference count of such an object
instance will remain zero.

If further handling is done only through the code that does not trigger the
reference counting mechanism, that reference will behave like a regular
one. Unless we call Free on it, it will leak since there will be no automatic
cleanup inserted when this unsafe reference goes out of scope.

However, if we call any code that triggers the reference counting


mechanism, the following will happen: the reference count will first be
increased to one, and when the code reaches the accompanying
reference count decrement, it will fall down to zero and that will trigger
the premature destruction of the object instance.
This is what the commonly used phrase: Don't mix object references with
interface references refers to. Mixing the two breaks the reference
counting mechanism.

To be more precise, you must not mix the two if the underlying object
instance has reference counting enabled. Also, taking temporary object
references after we have initially properly constructed an object instance
and stored it into at least one strong interface reference is allowed.
Again, such an object reference must not be used after the last strong
interface reference is cleared, as it would represent a stale reference.
type
IFoo = interface
procedure Foo;
end;

TFooBar = class(TInterfacedObject, IFoo)


public
procedure Foo;
procedure Bar;
end;

procedure CanMix;
var
Foo: IFoo;
FooBar: TFooBar;
begin
// Reference counted class must be stored in interface reference
// after construction.
Foo := TFooBar.Create;
Foo.Foo;

// Cast interface reference to temporary object instance


// for accessing other members.
FooBar := TFooBar(Foo);
FooBar.Bar;

// WRONG - don't explicitly release reference counted instance


// it will be managed automatically through Foo reference.
FooBar.Free;

// Assigning nil to interface reference will release the object instance


// before it goes out of scope assuming there are no other strong
// references to that instance. FooBar is weak reference.
// FooBar represents stale pointer at this point and should not
// be used afterwards
Foo := nil;
// WRONG - object instance is already gone
FooBar.Bar;
end;

If the object instance has reference counting disabled, we can safely mix
object and interface references. In other words, we can safely store
object instance in an object reference after construction and we can
safely use it where interface reference is required. The only thing we
have to keep in mind, is that we must make sure there are no interface
references with a lifetime longer than the lifetime of our object instance.
In other words, we must make sure that at the point where we call Free on
such an object instance, there are no active interface references that will
call _IntfClear when they go out of scope on an already dead object
instance - resulting in an access violation exception.

The most commonly used class where reference counting is disabled by


default is the TComponent class.

16.1.1 Supports kills object instances

One good example that shows why mixing object and interface
references is not the smartest idea ever, is the fact that some
System.SysUtils.Supports function overloads will kill the reference
counted object instance if the instance has a broken reference counting
mechanism. So merely testing whether the instance supports a particular
interface can end in disaster.

If the reference counted object instance is stored in an object reference


and passed to the Supports function, the underlying code will trigger the
reference counting mechanism and release the object instance during
that process.
type
TFoo = class(TInterfacedObject)
public
destructor Destroy; override;
end;

destructor TFoo.Destroy;
begin
Writeln('Foo is gone');
inherited;
end;

var
Foo: TFoo;
begin
Foo := TFoo.Create;
if Supports(Foo, IInterface) then
begin
end;
Writeln('Foo must be alive here');
// Foo.Free; // this would crash
end.

Running the above code will produce the following output, showing that
Foo is gone before its time. Furthermore, if we try to call Foo.Free after
calling Supports we will have an access violation exception because we will
try to release an already released object.
Foo is gone
Foo must be alive here

If we change the Foo reference to an interface reference, then the


reference counting mechanism will be preserved, and our instance will
survive the call to the Supports function and be automatically released
when the Foo reference goes out of scope. Since the only interface TFoo
supports is the basic IInterface , we cannot use any other interface to store
the TFoo interface reference. If TFoo implemented other interfaces, we could
use any of those instead of IInterface .
var
Foo: IInterface;

After making the above change we would get the expected output.
Foo must be alive here
Foo is gone

16.2 Disabling reference counting


Using interfaces as an abstraction tool in the classic compiler can be a
messy business. With simple classes that define simple interfaces, you
can use them as-is, enjoying the full benefits of reference counting. If the
class has many methods, or even worse, if it has many properties that
require separate getter and setter methods, interface declarations can
get out of hand. If you only need a few methods exposed through the
interface of an otherwise heavy class, you have two options - use a
reference counting class and cast the interface to a temporary object
when you need to access other functionality, or disable reference
counting.

Disabling reference counting is pretty straight forward. In order to use


interfaces in a class you have to implement three methods - QueryInterface ,
_AddRef and Release .

type
TNonManaged = class(TObject, IInterface)
protected
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;

function TNonManaged.QueryInterface(const IID: TGUID; out Obj): HResult;


begin
if GetInterface(IID, Obj) then
Result := 0
else
Result := E_NOINTERFACE;
end;

function TNonManaged._AddRef: Integer;


begin
Result := -1;
end;

function TNonManaged._Release: Integer;


begin
Result := -1;
end;

And then you can freely use such a class and its descendants through
object references. Of course, since automatic management is disabled,
you will have to explicitly release such object instances.
var
Obj: TNonManaged;
begin
Obj := TNonManaged.Create;
try
...
finally
Obj.Free;
end;
end;

16.3 Chameleon class


Tying a particular class to a specific memory management system,
especially if it is not the default one for the language, has always felt
wrong. It is not something that should be decided based on class, but
rather on the particular use case. If you use interfaces in a very limited
fashion, you can maybe get away with such limitations and choose either
a reference counted or non-counted option. If you use them a lot, sooner
or later you will be in a position where the particular memory model
forced within the class doesn't exactly fit the situation.

Since each class implements its own reference counting methods, this
gives you the opportunity to write reference counting methods in a
manner that will enable or disable counting depending on some
constructor parameters or other conditions. Reference counting methods
will still be inserted according to the rules and called, but if disabled, the
object instances will not be automatically released and will have to be
handled manually just like regular objects.

This kind of class basically must copy the TInterfacedObject implementation


and change it so that it can be disabled through some flag. So by starting
with the TInterfacedObject code as a template and adding the following
modifications, we will have a class that can be either reference counted
or not.
type
TManagedObject = class(TObject, IInterface)
// rest of the code is the same as in TInterfacedObject
protected
FManaged: boolean;
public
constructor Create; overload;
constructor Create(AManaged: boolean); overload;
end;
constructor TManagedObject.Create;
begin
inherited;
FManaged := false;
end;

constructor TManagedObject.Create(AManaged: boolean);


begin
inherited Create;
FManaged := AManaged;
end;

function TManagedObject._AddRef: Integer;


begin
if FManaged then
begin
// same code as in TInterfacedObject
end
else Result := -1;
end;

function TManagedObject._Release: Integer;


begin
if FManaged then
begin
// same code as in TInterfacedObject
end
else Result := -1;
end;

The only important thing to remember when using such a class is that if
reference counting is enabled, it must be stored within a strong interface
reference, and should not be manually released.
var
Obj: TManagedObject;
begin
// Reference counting is disabled
Obj := TManagedObject.Create;
try
...
finally
Obj.Free;
end;
end;

var
Intf: IInterface;
begin
// Reference counting is enabled
// will be released when last strong reference goes out of scope
Intf := TManagedObject.Create(true);
...
end;

is an example of a core RTL class with such dual memory


TXMLDocument
management. If created with some component as an Owner , its reference
counting will be disabled and the Owner will be responsible for its release. If
created without an Owner it will behave like any other reference counted
class and should be used through the IXMLDocument interface.
var
Doc: TXMLDocument;
begin
Doc := TXMLDocument.Create(Owner);
...
// will be released through Owner, or it can be manually released before that
Doc.Free;
end;

var
Doc: IXMLDocument;
begin
Doc := TXMLDocument.Create(nil);

// or

Doc := TXMLDocument.Create(FileName);

...
// will be automatically released when Doc goes out of scope
end;

16.4 Memory leak on const parameter


Disabling reference counting on parameters has an unfortunate side
effect. The fact that reference counted object instances are constructed
with a reference count of zero and that the vital initial reference count
increment happens during immediate assignment to an explicit or implicit
strong reference is at the core of this issue.

The problem arises when the reference counted object instance is


directly constructed as an inline parameter passed to a procedure. The
most prominent issue happens if such a parameter is marked as const or
[ref] with a disabled reference counting mechanism. But depending on
the code within the procedure, a value parameter can have a fatal
outcome too.

Unfortunately, this issue is not confined only to the classic compiler. The
ARC compiler also suffers from it, just with different, equally fatal
manifestations - depending on the particular compiler used.

This is a long standing issue, reported numerous times and


acknowledged by Barry Kelly, a former member of the Delphi compiler
team. Most recent report in Quality Portal is RSP-10100

Before covering variations and different behaviors on different compilers,


let's take a look at the simplest example necessary to reproduce some of
the issues - specifically the memory leak in the classic compiler.
Explaining why it happens in this case will actually explain all other cases
too. The cause of the defect is one, but the manifestations are different
due to different code being involved. Also, understanding the cause will
enable us to fix the issue properly so it will not resurface in the form of
some other issue later on when we add more code or make some
seemingly unrelated changes.
procedure LeakTest(const Intf: IInterface);
begin
end;

begin
ReportMemoryLeaksOnShutdown := true;
LeakTest(TInterfacedObject.Create);
end;

The above code will leak the TInterfacedObject instance constructed inline in
the procedure call. Removing the const modifier from the procedure
declaration will remove the leak.
procedure LeakTest(Intf: IInterface);

Don't ever use such a fix. It will just delay your problems, and not fix the
real cause.
So what is the problem here and how to properly solve it?

Let's start with the fix. It is fairly simple, don't construct an object inside
the procedure call, use an additional variable to construct the object first
and then pass it to the procedure.
procedure LeakTest(const Intf: IInterface);
begin
end;

var
Intf: IInterface;
begin
ReportMemoryLeaksOnShutdown := true;
Intf := TInterfacedObject.Create;
LeakTest(Intf);
end;

And now to the cause.

Marking a parameter as const means that no reference counting code will


be injected at the place of the call. This is what you want, right? Right.
But you are constructing a new object instance inline and since reference
counted instances are constructed with a reference count of zero, we are
counting on initial assignment to a strong reference to increase that count
and keep our object alive and properly counted. Guess what? That first
assignment to a strong reference is not happening here, because you are
passing the instance as a parameter that will not trigger any reference
counting. That is why the const parameter does not work and the default
value parameter does, since it triggers the reference counting
mechanism.

The effect you are seeing is the same you would have if you initially
stored your object instance in an object reference. The following example
leaks just the same if passed as a const parameter.
var
Obj: TInterfacedObject;
begin
ReportMemoryLeaksOnShutdown := true;
Obj := TInterfacedObject.Create;
LeakTest(Obj);
end;
Why is that a defect in the compiler? You clearly don't want reference
counting triggered in that procedure and that is exactly what is happening
here.

You are constructing a reference counting object that is not assigned


anywhere. The compiler knows that you are dealing with a reference
counted class because the parameter is declared as an interface. It also
knows that your instance is not previously assigned to anything, because
you are constructing it inline. Obviously at that point in time you don't
want to create leaking or crashing code. It is blatantly obvious that you
want reference counting properly initialized, and the compiler has all the
information it needs to create an implicit interface reference behind the
scenes and give your object instance a good start in life.

It is important to note that such code, constructing objects inline as


parameters is very common in languages with automatic memory
management. Even though the parameter has explicitly disabled
reference counting, this behavior is still unexpected. The fact that Delphi
does not support declaring inline variables, and you have to add them
somewhere before the begin block, makes this even more painful.

Well, there is another, less painful, solution to the problem when you are
dealing with interfaces. If you explicitly cast your object as an interface,
the compiler will add a hidden implicit reference and properly initialize
reference counting of that instance.
LeakTest(TInterfacedObject.Create as IInterface);

However, this still does not make this issue any less serious. First, you
have to know that this issue exists in order to use the proper solution.
That means every Delphi developer will experience this issue at some
point in time, get burned, waste more or less time and eventually learn
the hard way. Next, explicitly casting the object as interface does not
constitute a common coding pattern in Delphi. Unless you want some
other developer to jump in and correct your silly code without knowing
that you are working around a bug in the compiler, you will have to add a
comment explaining why this piece of code is written the way it is.

And even when you are aware of this issue, it is still very possible that, in
a moment of distraction, you will forget about it and write broken code.
Another issue with this kind of code is that you may be tempted to write it
only if the parameter is declared as const , and avoid duplicate counting
when dealing with value parameters. But const parameters are merely an
optimization feature when it comes to reference counted instances, and
they are subject to change. As result, your code can stop working
properly if someone somewhere tweaks some procedure declaration.

Since this issue has many variations, using the proper solutions in
various places can lead to fragile code that can easily be broken. Instead
of keeping track of parameters and their declarations, there is one simple
rule you can use instead. Don't construct objects inline and always use
an additional variable. This is easy to remember and easy to follow.

A more elaborate example that exhibits not only leaks, but also crashes,
consists of a default Multi Device Application, with four buttons and a
memo. Add the following code and observe the effects. After each button
click, properly working code will add a Foo destroyed line to the memo.
Hint, the UseConstReference procedure will result in a crash.
TFoo = class(TInterfacedObject)
public
destructor Destroy; override;
end;

destructor TFoo.Destroy;
begin
Form1.Memo1.Lines.Add('Foo destroyed');
inherited;
end;

procedure Reference(Param: IInterface);


begin
end;

procedure ConstReference(const Param: IInterface);


begin
end;

procedure UseReference(Param: IInterface);


var o: TFoo;
begin
Form1.Memo1.Lines.Add('Using Foo');
o := TFoo(Param);
end;
procedure UseConstReference(const Param: IInterface);
begin
UseReference(Param);
UseReference(Param);
end;

procedure TForm1.Button1Click(Sender: TObject);


begin
Reference(TFoo.Create);
end;

procedure TForm1.Button2Click(Sender: TObject);


begin
ConstReference(TFoo.Create);
end;

procedure TForm1.Button3Click(Sender: TObject);


begin
UseReference(TFoo.Create);
end;

procedure TForm1.Button4Click(Sender: TObject);


begin
UseConstReference(TFoo.Create);
end;

16.5 Creating a fake interface


We all know records come uninitialized by default. That means they can
hold any random garbage inside. If you want to have a record with some
field initialized to some default value, even if it is a plain zero, you simply
cannot count on that. In some cases, relying on manual initialization can
defeat the whole purpose of having records as well encapsulated
wrappers that will not allow developers to make silly mistakes.

This is where managed types come in. They are always initialized no
matter where they are stored and we can use them as a properly
initialized flag inside the records. Since strings are managed types they
can be used for implementing such flags.
type
TFlagable = record
private
FFlag: string;
function GetIsSet: boolean;
procedure SetIsSet(Value: boolean);
public
property IsSet: boolean read GetIsSet write SetIsSet;
end;

function TFlagable.GetIsSet: boolean;


begin
Result := FFlag <> '';
end;

procedure TFlagable.SetIsSet(Value: boolean);


begin
if Value then FFlag := '_'
else FFlag := '';
end;

var
Flagable: TFlagable;
begin
Writeln(Flagable.IsSet);
end;

No matter where you declare a TFlagable variable, even if it is a local one


running upon some dirty stack, Flagable.IsSet will always start as False until
you explicitly set it to True .

If you need more than two states, you can appropriately set different
values into the FFlag string.

However, if the flag only has two states, an interface can be used instead
of a string.
type
TFlagable = record
private
FFlag: IInterface;
function GetIsSet: boolean;
procedure SetIsSet(Value: boolean);
public
property IsSet: boolean read GetIsSet write SetIsSet;
end;

function TFlagable.GetIsSet: boolean;


begin
Result := FFlag <> nil;
end;
procedure TFlagable.SetIsSet(Value: boolean);
begin
if Value then FFlag := TInterfacedObject.Create
else FFlag := nil;
end;

The above implementation will work just as the string one, but creating an
object on the heap is a slightly wasteful approach. Fortunately, to
implement an interface all you need are three methods and a virtual
method table. Since in this case you don't need any actual reference
counting to take place, this functionality can be easily achieved by some
plain functions and array that will simulate an interface VMT. The first
parameter in those functions serves as the implicit Self parameter that all
methods have.
function NopAddRef(inst: Pointer): integer; stdcall;
begin
Result := -1;
end;

function NopRelease(inst: Pointer): integer; stdcall;


begin
Result := -1;
end;

function NopQueryInterface(inst: Pointer; const IID: TGUID; out Obj): HResult;


begin
Result := E_NOINTERFACE;
end;

const
FakeInterfaceVTable: array [0 .. 2] of Pointer =
(@NopQueryInterface, @NopAddRef, @NopRelease);
FakeInterfaceInstance: Pointer = @FakeInterfaceVTable;

procedure SetFakeInterface(var Intf: IInterface);


begin
Intf := IInterface(@FakeInterfaceInstance);
end;

And all we need to do is change the SetIsSet implementation and we will


have a flagging mechanism that wastes no memory, induces no locks
and runs as fast as possible.
procedure TFlagable.SetIsSet(Value: boolean);
begin
if Value then SetFakeInterface(FFlag)
else FFlag := nil;
end;

16.6 Interface list


Besides storing interfaces in arrays, the classic compiler has few options
for storing interfaces in a list. TList and TObjectList are not suitable for
maintaining a list of strong interface references because they actually
maintain a list of raw pointers that don't trigger reference counting and
cannot hold strong references.

16.6.1 System.Classes.TInterfaceList

is a class that can be used to maintain a list of strong


TInterfaceList
interface references. There is not much special to be said about it, except
one thing. TInterfaceList is itself a reference counted class. That means
that it should be stored in an interface reference and not an object
reference, and its release will be automatically handled. This is a rather
common mistake, because other base RTL list classes are not reference
counted.
var
List: IInterfaceList
Intf: IInterface;
begin
List := TInterfaceList.Create;

Intf := TInterfacedObject.Create;
List.Add(Intf);
Intf := TInterfacedObject.Create;
List.Add(Intf);

Writeln(List.Count);
end.

maintains a list of IInterface references. That means if you


TInterfaceList
need a particular interface type you will have to retrieve the actual
interface using the as operator or using the Supports function.
uses
System.Classes,
System.SysUtils;
type
IFoo = interface
['{E46B52B5-78EA-41AF-9C44-9FD59FA1A7F8}']
procedure Foo;
end;

IBar = interface
['{180296D2-CCB8-4033-9F47-4D70434A9721}']
procedure Bar;
end;

TFooBar = class(TInterfacedObject, IFoo, IBar)


public
procedure Foo;
procedure Bar;
end;

procedure TFooBar.Bar;
begin
Writeln('Bar');
end;

procedure TFooBar.Foo;
begin
Writeln('Foo');
end;

var
List: IInterfaceList;
Intf: IInterface;
Foo: IFoo;
begin
List := TInterfaceList.Create;

Intf := TFooBar.Create;
List.Add(Intf);
Intf := TFooBar.Create;
List.Add(Intf);
Intf := TFooBar.Create;
List.Add(Intf);

Intf := List[0];

(Intf as IFoo).Foo;
(Intf as IBar).Bar;

if Supports(Intf, IFoo, Foo) then Foo.Foo;
end.
The above code will output:
Foo
Bar
Foo

16.6.2 System.Generics.Collections.TList<T>

For easier access to stored references that does not need any interface
querying, a generic TList<T> can be used instead of TInterfaceList . Since
TList<T> is not a reference counted class, we have to manually release the
list when we are done.
var
List: TList<IFoo>;
Intf, Foo: IFoo;

begin
ReportMemoryLeaksOnShutdown := true;
List := TList<IFoo>.Create;
try
Intf := TFooBar.Create;
List.Add(Intf);
Intf := TFooBar.Create;
List.Add(Intf);
Intf := TFooBar.Create;
List.Add(Intf);

Foo := List[0];
Foo.Foo;
finally
List.Free;
end;
end.
17 Anonymous methods - a trouble
without a name
Delphi has two procedural types capable of storing and executing plain
functions and procedures, as well as methods - functions and procedures
declared within the context of a class.

Anonymous methods add a third one. As their name suggests, they are
methods that do not have an associated identifier - they have no name.
They represent blocks of code that can be assigned to a variable or
passed as a parameter and executed later on, in the same manner as
other procedural types. Anonymous methods are similar to closures in
other languages, as they implement variable capture, which enables the
usage of variables from the context in which they are defined.

The type declaration of anonymous methods follows the rules for


declaration of plain functions and procedures, prefixed with reference to ,
and is called a method reference type.
type
TAnonymousProc = reference to procedure;
TAnonymousFunc = reference to function: Integer;

TAnonymousProcWithParameters = reference to procedure(x, y: Integer);


TAnonymousFuncWithParameters = reference to function(x, y: Integer): Integer;

17.1 How are anonymous methods implemented?


Capturing variables from an outside context implies extending their
lifetime, as they may be long gone by the time the anonymous methods
get to use them. Capturing the variables and extending their lifetime is
only one side of the capturing mechanism - it also needs to perform
proper cleanup after the variables are no longer needed.

Delphi already uses reference counting to maintain the lifetime of some


types, so using automatic reference counting as the lifetime management
mechanism in anonymous methods is a logical choice.

Anonymous methods are basically defined as interfaces with a single


method - Invoke - implemented by a hidden reference counted class, and
captured variables are stored as fields of that class. When an anonymous
method is accessed, an instance of that class is constructed behind the
scenes, and it is kept alive through reference counting for as long as is
required by the anonymous method it wraps.

If we translated the previously declared method reference types into the


appropriate interface declarations they would look like this:
type
IAnonymousProc = interface
procedure Invoke;
end;

IAnonymousFunc = interface
function Invoke: Integer;
end;

IAnonymousProcWithParameters = interface
procedure Invoke(x, y: Integer);
end;

IAnonymousFuncWithParameters = interface
function Invoke(x, y: Integer): Integer;
end;

Variable capture solved, and they all lived happily ever after.

Well no, not really. It all works well, until you start capturing reference
counted object instances exposed through interfaces in the classic
compiler, or all instances in the ARC compiler. Actually, you can create
nice cycles with records, too.

Anonymous methods (closures) in combination with ARC can form strong


reference cycles and cause memory leaks. That is an inevitable side-
effect of the inner workings of the reference counting mechanism. This is
not a Delphi specific issue, though the Delphi implementation throws a
few curveballs of its own.

A strong reference cycle is created when a class (or record) stores a


closure in its field, and at the same time that closure captures some other
fields or calls some other method from that class. That is called capturing
self, and it is an inevitable consequence of capturing variables from the
surrounding context in combination with the reference counting
mechanism. Besides cycles created between an object instance (or
record) and an anonymous method, cycles can also be created when an
anonymous method references itself - the variable where it is stored,
even if it is a local variable.

Delphi also suffers from some issues that make the problems related to
anonymous method cycles more prominent:

Missing language features - Delphi does not have so-called capture


lists, which allow you to specify weak or unsafe captures for Self or
other variables. Also, capturing Self in Delphi is less obvious. In
Swift, for instance, you have to explicitly write self if you are referring
to any self-contained fields, methods or properties, or you will get a
compiler error. That makes accidental self-captures virtually non-
existent.

Issues caused by some anonymous method implementation details.


Due to the specific implementation used in Delphi, anonymous
methods tend to capture way more or way less than they should, and
in a more convoluted way than is expected of them. Explicitly
capturing self does not always create cycles, and using the [weak]
attribute does not break cycles as you might expect. Using
anonymous methods in combination with reference counted object
instances requires some caution. For instance, if more anonymous
methods are defined in the same context, they will be backed up by
the same class, opening up the possibility of less-obvious reference
cycles.

Bugs and issues in core or other frameworks due to used method


references not being released as soon as possible. Such issues can
happen in any code, but in the context of Delphi, some issues stem
from the fact that the RTL core has only recently started taking
advantage of anonymous methods to implement some features.

If one were to write a list of features where the lack of a language


specification makes using a particular feature really hard, anonymous
methods would certainly be at the top of that list.

Since there is no language specification, it is also impossible to tell


whether a particular observed behavior is well-defined and works as
designed, or it is merely a side-effect of some bug or other issue. That
means additional caution is needed when upgrading to a new version,
because that behavior might change.

While the mechanism behind anonymous methods themselves is pretty


clear, its interaction with ARC-enabled code can be somewhat blurry at
times.

17.2 Anonymous method variable capture


Let's shed some light on the variable capture process. After all, this is the
process that can create reference cycles. Obviously, capturing locally
declared integers, strings and similar types cannot have a negative
impact on memory management in any way, nor can it create reference
cycles. But, before one can run, one should learn how to walk.

contains a number of predefined anonymous method types,


System.SysUtils
but to make the code easier to follow, I will declare the appropriate
anonymous method types and some utility methods, instead of using pre-
declared types.
type
TAnonymousProc = reference to procedure;

TAnonymousStringFunc = reference to function: string;

procedure Execute(Proc: TAnonymousProc);


begin
// Execute passed anonymous method
Proc;
end;

The first example is a bit contrived, as there is really no need to use


anonymous methods in this manner. Running the code would produce
the same result as we would get if we just skipped all the anonymous
methods and directly used a local variable.
...
procedure Test;
var
Number: Integer;
begin
Number := 42;

Execute(procedure
begin
Writeln('Anything ', Number);
end);

Number := 0;

Execute(procedure
begin
Writeln('Bang ', Number);
Number := 42;
end);

Execute(procedure
begin
Writeln('Resurrected ', Number);
end);
end;

begin
Test;
end.

Anything 42
Bang 0
Resurrected 42

Not only can we capture a variable, but we can change its value from
within the anonymous methods.

Let's change the Test method, and instead of executing methods inline,
store them into variables for later execution:
procedure Test;
var
Number: Integer;
Proc1, Proc2, Proc3: TAnonymousProc;
begin
Number := 42;

Proc1 := procedure
begin
Writeln('Anything ', Number);
end;

Number := 0;

Proc2 := procedure
begin
Writeln('Bang ', Number);
Number := 42;
end;

Proc3 := procedure
begin
Writeln('Resurrected ', Number);
end;

Execute(Proc1);
Execute(Proc2);
Execute(Proc3);
end;

Anything 0
Bang 0
Resurrected 42

Now what? Why is Anything 0 printed? Well, obviously, for the same reason
that we were able to change values from within the anonymous methods
- because we captured the location of the variable and not its value.
Before we called any of our anonymous methods, we executed the
following code:
Number := 42;
Number := 0;

That is why when we started executing anonymous methods, the value in


Number was 0 . Whatever you do with the captured variable inside or outside
anonymous methods will influence that variable in order of execution, not
order of definition.

To move things further, let's declare Proc1 , Proc2 and Proc3 outside the Test
procedure and call them outside its scope.
var
Proc1, Proc2, Proc3: TAnonymousProc;
procedure Test;
var
Number: Integer;
begin
Number := 42;

Proc1 := procedure
begin
Writeln('Anything ', Number);
end;

Number := 0;

Proc2 := procedure
begin
Writeln('Bang ', Number);
Number := 42;
end;

Proc3 := procedure
begin
Writeln('Resurrected ', Number);
end;
end;

begin
Test;
Execute(Proc1);
Execute(Proc2);
Execute(Proc3);
end.

Anything 0
Bang 0
Resurrected 42

As expected, we had the same output as when executing anonymous


methods within the Test procedure, as they have captured the local Number
variable and extended its lifetime.

Now that it is clearer how capture works, we can understand why


capturing object instances will not work as we might wish, at least not
when they are not backed up by the reference counting mechanism.
Anonymous methods can extend the lifetime of the reference, but they
cannot extend the lifetime of the associated object instance itself.

The following is a simple example where composing strings is done


within the anonymous method. By using different types of local variables,
we can observe and compare their behavior.

What exactly are we testing in the Test procedure? First, we will acquire
an anonymous method by using the Composer function. Then, we will
execute that function two times to observe what is happening with the
captured variables, and then we will acquire the anonymous method
again and execute it once - merely to prove that each time we call the
anonymous method factory, we will be served with a brand new
anonymous method, which captured a fresh set of local variables. This is
expected behavior, because every time you call a function, a new stack
frame will be created to serve that call, and local variables will be
allocated on that stack frame.
function Composer: TAnonymousStringFunc;
var
Text: string;
Number: Integer;
begin
Text := 'Number';
Number := 5;
Result := function: string
begin
Inc(Number);
Result := Text + ' ' + IntToStr(Number);
end;
end;

procedure Test;
var
Ref1, Ref2: TAnonymousStringFunc;
Str: string;
begin
// Get anonymous method
Ref1 := Composer();
// Call anonymous method
Str := Ref1;
Writeln(Str);
// Call anonymous method
Str := Ref1;
Writeln(Str);

// Get anonymous method again


Ref2 := Composer();
// Call new anonymous method
Str := Ref2;
Writeln(Str);

// Call first anonymous method again


Str := Ref1;
Writeln(Str);
end;

begin
ReportMemoryLeaksOnShutdown := true;
Test;
end.

Number 6
Number 7
Number 6
Number 8

Looking at the output confirms what we already know. The anonymous


method captures local variables and extends their lifetime beyond the
lifetime they would have had while confined to the local function call - that
is visible from the first two lines. The third one shows that the new
anonymous method comes with a new set of variables, and the last one
confirms that the variables captured by the first method are not
influenced by the second one. They are independent sets.

Records are value types, so capturing records should work just about the
same way:
TContentRec = record
Text: string;
Number: Integer;
constructor Create(const AText: string; ANumber: Integer);
end;

constructor TContentRec.Create(const AText: string; ANumber: Integer);


begin
Text := AText;
Number := ANumber;
end;

function Composer: TAnonymousStringFunc;


var
Content: TContentRec;
begin
Content := TContentRec.Create('Number', 5);
Result := function: string
begin
Inc(Content.Number);
Result := Content.Text + ' ' + IntToStr(Content.Number);
end;
end;

Calling our Test procedure will yield the same result:


Number 6
Number 7
Number 6
Number 8

Now let's try objects. As we already know, changes to captured variables


happen sequentially, following the code's order of execution. The problem
with objects is that we have to manage their memory. When should we
release the local object instance, and is it even possible to use it in this
manner?
TContentObject = class(TObject)
public
Text: string;
Number: Integer;
constructor Create(const AText: string; ANumber: Integer);
end;

constructor TContentObject.Create(const AText: string; ANumber: Integer);


begin
Text := AText;
Number := ANumber;
end;

function Composer: TAnonymousStringFunc;


var
Content: TContentObject;
begin
Content := TContentObject.Create('Number', 5);
Result := function: string
begin
Inc(Content.Number);
Result := Content.Text + ' ' + IntToStr(Content.Number);
end;
end;

Calling Test again shows the same output. Good. But we leaked two
TContentObject instances. Not so good.
Actually, the above code leaks in the classic compiler, but not in ARC,
where all object instances are reference counted and their memory is
automatically managed.
Number 6
Number 7
Number 6
Number 8

Let's try to handle the leak in the classic compiler.


function Composer: TAnonymousStringFunc;
var
Content: TContentObject;
begin
Content := TContentObject.Create('Number', 5);
Result := function: string
begin
Inc(Content.Number);
Result := Content.Text + ' ' + IntToStr(Content.Number);
end;
Content.Free;
end;

No leak, but what happened to our output? Well, we are accessing a


dangling reference since by the time we call the function, our Content
object instance has been released. Since the string variable is managed
by the compiler, it has been cleaned after the instance was released, and
the Text field contains an empty string. On the other hand, the integer field
Number will hold a leftover value.

6
2
6
2

If you are still not convinced we are dealing with a dangling pointer, just
nil the Content variable and enjoy the access violation exception that
causes.
...
Content.Free;
Content := nil;

So, this approach obviously does not work. What if we Free the Content
from within the body of the anonymous method? By now, it should be
pretty obvious that that approach will not work either, but...
function Composer: TAnonymousStringFunc;
var
Content: TContentObject;
begin
Content := TContentObject.Create('Number', 5);
Result := function: string
begin
Inc(Content.Number);
Result := Content.Text + ' ' + IntToStr(Content.Number);
Content.Free;
Content := nil;
end;
end;

The end result - Number 6 , followed by an access violation exception


caused by the second execution of the anonymous method. Well, at least
the first call produced an appropriate result.

If we also add a different Test2 procedure that only executes each


anonymous method once, we will get the correct result and no leaks.
procedure Test2;
var
Ref1, Ref2: TAnonymousStringFunc;
Str: string;
begin
Ref1 := Composer();
Str := Ref1;
Writeln(Str);

Ref2 := Composer();
Str := Ref2;
Writeln(Str);
end;

Generally speaking, there is no reason to use this kind of code. We


cannot reuse a locally constructed object, and if we really need one, we
should move it to the body of the anonymous method. That way, it will not
matter how many times we have to call the procedure.
function Composer: TAnonymousStringFunc;
begin
Result := function: string
var
Content: TContentObject;
begin
Content := TContentObject.Create('Number', 5);
Inc(Content.Number);
Result := Content.Text + ' ' + IntToStr(Content.Number);
Content.Free;
end;
end;

Of course, if you want to have the same output as before, 6 7 6 8 , you will
not like this one:
Number 6
Number 6
Number 6
Number 6

If you need to have a local object instance for whatever reason, and you
also need to update its fields, you will have to add additional local
variables (value types) that can handle being captured by the anonymous
methods.
function Composer: TAnonymousStringFunc;
var
Number: Integer;
begin
Number := 5;
Result := function: string
var
Content: TContentObject;
begin
Inc(Number);
Content := TContentObject.Create('Number', Number);
Result := Content.Text + ' ' + IntToStr(Content.Number);
Content.Free;
end;
end;

And the expected output is back again:


Number 6
Number 7
Number 6
Number 8

There is another solution to our object instance problem. If the object


instance we have to construct locally is reference counted, its memory
will be automatically managed. The anonymous method will be capable
of holding that instance and releasing it when it is no longer needed.
Since this is a locally created object instance that does not reference the
anonymous method in any way, there is no possibility of creating
reference cycles.

The only thing we have to keep in mind here is that a locally constructed
object instance will be kept alive as long as the anonymous method
referencing it is alive. In our example, that will happen in the epilogue of
the Test procedure. If we needed to release it sooner, we would have to
set the Refxxx variable holding that particular instance to nil .

Also, if an anonymous method reference is passed on to other parts of


the framework that might hold that reference, or if we move that
reference declaration into the global scope, our local object might not be
promptly released.

Keeping track of and knowing what happens with your anonymous


method references when interacting with other code is important, as that
code might have issues or bugs of their own that will prevent the timely
release of your method and all captured variables.
TContentObject = class(TInterfacedObject)
public
Text: string;
Number: Integer;
constructor Create(const AText: string; ANumber: Integer);
end;

function Composer: TAnonymousStringFunc;


var
Intf: IInterface;
begin
Intf := TContentObject.Create('Number', 5);
Result := function: string
var
Content: TContentObject;
begin
Content := TContentObject(Intf);
Inc(Content.Number);
Result := Content.Text + ' ' + IntToStr(Content.Number);
end;
end;
17.3 Anonymous method reference cycles
As previously mentioned, variable capture can cause reference cycles. If
you avoid capturing variables you will successfully avoid creating cycles.
:)

If you think that was just a joke... well, it is a joke, but only to a point.
Capturing variables is a powerful feature, but also one that can easily be
abused, leading to bad code, regardless of cycles. So when you use
anonymous methods and their ability to capture variables from the
surrounding context, make sure that you are not making a real mess of
your code along the way.

Probably the most devious reference cycles are ones created with
several anonymous methods. Other kinds of reference cycles involving
anonymous methods are more obvious and are more easily recognized,
as they resemble reference cycles created by regular strong references
to object instances. You just have to remember that any anonymous
method reference is an interface reference in disguise.

And now, let's make some cycles...


procedure Test;
var
Proc1, Proc2: TAnonymousProc;
begin
Proc1 := procedure
begin
Writeln('Procedure 1');
end;

Proc2 := procedure
begin
Proc1;
Writeln('Procedure 2');
end;

Proc2;
end;

begin
ReportMemoryLeaksOnShutdown := true;
Test;
end.

The above code will leak the hidden object instance used to back up
those two anonymous methods. This leak happens because of specific
implementation details - in this case, the fact that both of our methods are
backed up by the same hidden object instance. Since the second method
references the first one, it effectively references its own supporting
instance.

Since Proc1 is the one causing the cycle, we can solve this leak by
explicitly setting it to nil before the end of the local procedure.
...
Proc2;
Proc1 := nil;
end;

Even if we removed the explicit Proc2 variable, we would still create a leak,
because the parameter passed to the Execute procedure would be
implicitly captured within the same context.
procedure Test;
var
Proc1: TAnonymousProc;
begin
Proc1 := procedure
begin
Writeln('Procedure 1');
end;

Execute(procedure
begin
Proc1;
Writeln('Procedure 2');
end);
end;

However, if we move the first procedure to the second one, they will be
defined in different code blocks and supported by two different instances.
procedure Test;
begin
Execute(procedure
begin
Execute(procedure
begin
Writeln('Procedure 1');
end);
Writeln('Procedure 2');
end);
end;

Coming next, a cycle created by a record or class storing a reference to


an anonymous method that captures other members of that record or
object instance.
type
TAnonymousRec = record
Number: Integer;
Proc: TAnonymousProc;
end;

procedure Test;
var
Rec: TAnonymousRec;
begin
Rec.Number := 5;
Rec.Proc := procedure
begin
Writeln(Rec.Number);
end;
Rec.Proc();
end;

begin
ReportMemoryLeaksOnShutdown := true;
Test;
end.

Since records are value types, the above code will create a cycle
because the whole record value (holding a reference to the anonymous
method) will be captured. Converting TAnonymousRec to a class will behave
differently in the classic and ARC compilers, because the reference
counting mechanism would create a strong reference cycle between the
object instance and the anonymous method that captured that instance.
type
TAnonymousObject = class(TObject)
public
Number: Integer;
Proc: TAnonymousProc;
end;

Classic compiler version without the leak:


procedure Test;
var
Obj: TAnonymousObject;
begin
Obj := TAnonymousObject.Create;
try
Obj.Number := 5;
Obj.Proc := procedure
begin
Writeln(Obj.Number);
end;
Obj.Proc();
finally
Obj.Free;
end;
end;

ARC version with the leak:


procedure Test;
var
Obj: TAnonymousObject;
begin
Obj := TAnonymousObject.Create;
Obj.Number := 5;
Obj.Proc := procedure
begin
Writeln(Obj.Number);
end;
Obj.Proc();
end;

Obviously, Proc is the culprit here and should be nilled to break the cycle.
But there is one thing we can exploit here. The ARC version leaks in the
ARC compiler, but if we use the classic compiler version, it does not leak
in the ARC compiler. The secret is in the Obj.Free call, which translates to
Obj := nil in the ARC compiler, effectively breaking the cycle. Of course,
from an ownership point of view, it is the wrong way to break the cycle,
but since the classic compiler code works well in both compilers, thus
making such code cross-platform, it is acceptable.
Using a reference counted class through an interface reference would
also create the same kind of leak, that can also be resolved by setting the
reference to nil before the end of the local procedure. However, while
calling Free on an object instance would not raise any eyebrows, nilling an
interface reference just before it goes out of scope might. It would be
prudent to comment such code to prevent someone from clearing it out
by mistake, as it might seem like redundant cruft.

If you cannot wrap your head around why setting an object or interface
reference to nil immediately before it goes out of scope can clear the
above cycle, the secret is in the implicit code added by the compiler in
the epilogue - another call to _InstClear or _IntfClear to finalize the local
variable as covered in the Strong reference initialization and finalization
chapter. Because of this, the reference count of the Obj instance will be
decreased twice, and that is what breaks the cycle in this code.

The previously presented code not only has obvious flaws, but it is also
quite pointless. Its purpose is not to be a pattern you would use, but to be
a learning model.

A commonly used pattern used with anonymous methods is to not


execute them immediately after defining them; they are usually either
executed at a later time or executed asynchronously. In that case, you
cannot rely on a non-reference counted object instance being alive when
the anonymous method executes, and depending on the situation it might
not be possible to release that object from within the anonymous method.

In such cases you have to either use reference counted object instances,
or you must release the object from within the method; but in the latter
case you must call such an anonymous method only once.

In terms of the previous example, if we imagine that the code within


Obj.Proc was executed asynchronously under manual memory
management, then calling Obj.Free after we started execution of Obj.Proc
would release the Obj instance before the anonymous method had the
chance to complete.

Such an asynchronous scenario can be created with TTask from the RTL
Parallel Programming Library.
type
TProcessor = class(TObject)
protected
FData: TData;
...
public
procedure Run;
end;

procedure TProcessor.Run;
var
Task: ITask;
begin
FData.Prepare;
Task := TTask.Create(procedure
begin
FData.Process;
FData.Processed := true;
end);
Task.Start;
end;

The above Run method will not create any strong cycles even though we
are accessing TProcessor fields inside an anonymous method, because Task
is a local variable. However, if for any reason we have to make that Task
variable accessible to a broader context, even temporarily, we will have a
strong reference cycle.

In such a case, we have to explicitly break the cycle at the appropriate


point. In the following example, it is logical to clear the FTask field after the
task has completed its job, from within the anonymous method.
type
TProcessor = class(TObject)
protected
FData: TData;
FTask: ITask;
...
end;

procedure TProcessor.Run;
begin
FData.Prepare;
FTask := TTask.Create(procedure
begin
FData.Process;
FData.Processed := true;
FTask := nil;
end);
FTask.Start;
end;

17.4 Using weak references to break anonymous


method reference cycles
A strong reference cycle is a strong reference cycle regardless of how it
is created. Breaking strong reference cycles created by anonymous
methods is not too different from breaking other kinds of strong
references. Besides explicitly breaking cycles at a certain point during
execution, you can figure out which reference is the owning one and
which one is not, and make the non-owning one weak, or instead of
capturing a strong variable you can capture a weak one.

Of course, capturing weak instead of strong can only be used if the


lifetime of the original reference is longer than the lifetime needed for the
captured variable, or the required functionality allows you to have nil
checks inside the anonymous method and do nothing if the original
reference is already gone.

If the above requirement cannot be satisfied, you will have to break the
cycle by some other method: separating dependencies, limiting the scope
of anonymous methods if you have more of them, using different
transient variables for storing values that must be captured...

If you can break a cycle by weak references, great; you just have to use
standard methods for making weak references and you are done...

Not so fast.

You can use [unsafe] or you can use a pointer, but you cannot use the
[weak] attribute because the compiler likes them strong.

17.4.1 Compiler turns a weak reference captured by anonymous


method into strong one

QP report RSP-19204
program RSP_19204;

{$APPTYPE CONSOLE}

uses
System.SysUtils;

type
TAnonymousProc = reference to procedure;

ICounted = interface
function GetRefCount: Integer;
end;

TCounted = class(TInterfacedObject, ICounted);

procedure TestUnsafeProc;
var
Intf: ICounted;
[unsafe] u: ICounted;
Proc: TAnonymousProc;
begin
Intf := TCounted.Create;
u := Intf;

Proc := procedure
begin
Writeln('Unsafe proc ', u.GetRefCount);
end;

Proc();
end;

procedure TestWeakProc;
var
Intf: ICounted;
[weak] w: ICounted;
Proc: TAnonymousProc;
begin
Intf := TCounted.Create;

// this assignment is broken


w := Intf;

Proc := procedure
begin
Writeln('Weak proc ', w.GetRefCount);
end;

Proc();
end;

procedure TestWeak;
var
Intf: ICounted;
[weak] w: ICounted;
begin
Intf := TCounted.Create;
w := Intf;
Writeln('Weak ', w.GetRefCount);
end;

begin
TestWeak;
TestUnsafeProc;
TestWeakProc;
end.

Running the above code produces the following output:


Weak 1
Unsafe proc 1
Weak proc 2

So, what happened here? References marked with [weak] attribute must
not contribute to reference counting. They should merely be tracked and
zeroed when the object instance is destroyed. However, that is not what
happens here. The reference count in all three test cases should be one,
but it is not when the weak reference is touched by an anonymous
method.

If we inspect the generated code for the weak reference assignment w :=


Intf; , we will see that it uses _IntfCopy for assigning, effectively making the
w reference strong.

RSP_19204.dpr.43: w := Intf;
0041B769 8B45F4 mov eax,[ebp-$0c]
0041B76C 83C00C add eax,$0c
0041B76F 8B55FC mov edx,[ebp-$04]
0041B772 E8FDF3FEFF call @IntfCopy

Commenting out the anonymous method, or changing it to capture the


Intf variable instead,

w := Intf;
Proc := procedure
begin
Writeln('Weak proc ', Intf.GetRefCount);
end;

will result in the following code, where _IntfWeakCopy is used for assigning:
RSP_19204.dpr.43: w := Intf;
0041B6CC 8D45FC lea eax,[ebp-$04]
0041B6CF 8B55F4 mov edx,[ebp-$0c]
0041B6D2 8B520C mov edx,[edx+$0c]
0041B6D5 E8DA05FFFF call @IntfWeakCopy

Since the compiler breaks the [weak] attribute for references captured by
anonymous methods, you cannot use it for breaking strong reference
cycles. What makes this rather unfortunate is that if you are not sure that
the lifetime of the captured object instance will be longer than the lifetime
of the anonymous method, you cannot use [unsafe] or pointers either, or
you can end up having mysterious crashes or bugs when you access a
dangling pointer.

While the previous example does not actually create any leaks because it
does not have any strong reference cycles, it is rather easy to create one:
type
TLeaked = class(TInterfacedObject)
public
Proc: TAnonymousProc;
end;

procedure Test;
var
Intf: IInterface;
[weak] w: IInterface;
begin
Intf := TLeaked.Create;
w := Intf;

TLeaked(Intf).Proc := procedure
begin
if Assigned(w) then
end;
end;

begin
ReportMemoryLeaksOnShutdown := true;
Test;
end.

However, there is still something that works in such scenarios: Using any
kind of weak wrapper, like the one mentioned in the Storing weak and
unsafe references in collections chapter, or the one in the Coding
patterns - Weak chapter.
w: IWeak<IInterface>;
begin
Intf := TLeaked.Create;
w := TWeak<IInterface>.Create(Intf);

While the above issue is presented through interface references and can
be reproduced in the classic compiler, the same issue can be observed in
the ARC compiler using either interface or object references.

17.5 Storing weak and unsafe references in collections


Only individual references can be marked as [weak] and [unsafe] . Arrays
keep strong references, and since they are commonly used on the
implementation side of various collections, any stored reference will
automatically be a strong one.

A common practice in the classic Delphi compiler for avoiding the


reference counting mechanism on interface references was to store them
as plain pointers. If you needed more than one, you would use an array
(collection) of pointers.

Delphi Berlin 10.1 introduced support for the [weak] and [unsafe] attributes
in the classic compiler. This improved the handling of [unsafe] interface
references. Using pointers as a workaround is no longer necessary, and
having auto-zeroed [weak] interface references opened up new
possibilities in handling complex scenarios with reference counted
objects.

While using pointers as a non-type safe workaround for storing [unsafe]


references in collections can be done on all compilers, storing [weak]
references still poses a bit of a problem.
A solution to the array of weak references comes in the form of a wrapper
around references we want to be either [weak] or [unsafe] . Since the
wrapper implementations for [weak] and [unsafe] differ only in the used
attribute, and storing [weak] is the primary issue, the following examples
will use the [weak] attribute. In Delphi, there are two options for wrappers -
using records or objects.

Record-based implementation for a [weak] wrapper:


type
TWeakRec<T: class> = record
public
[weak] Value: T;
constructor Create(const AValue: T);
end;

constructor TWeakRec<T>.Create(const AValue: T);


begin
Value := AValue;
end;

Class-based implementation for a [weak] wrapper:


type
TWeakObj<T: class> = class(TObject)
public
[weak] Value: T;
constructor Create(const AValue: T);
end;

constructor TWeakObj<T>.Create(const AValue: T);


begin
Value := AValue;
end;

And weak wrappers for interface references:


type
TWeakIntfRec<T: IInterface> = record
public
[weak] Value: T;
constructor Create(const AValue: T);
end;

constructor TWeakIntfRec<T>.Create(const AValue: T);


begin
Value := AValue;
end;

type
TWeakIntfObj<T: IInterface> = class(TObject)
public
[weak] Value: T;
constructor Create(const AValue: T);
end;

constructor TWeakIntfObj<T>.Create(const AValue: T);


begin
Value := AValue;
end;

There is another issue that has to be considered when implementing


wrappers. The default comparers used for finding and comparing
references will no longer work on wrapped references, as they will
compare wrappers instead. If there is a need to compare wrapped
references, we also need to provide a custom comparer.

17.5.1 Records vs Object

This comparison is not specific to weak wrappers, but wrappers in


general. There are a few things to consider. One is the wrapper's storage
requirements, and the other is whether or not it is required to have only
one wrapper around a particular instance. Records are value types, and
passing a record around will make copies of the contents of that record
(without making a deep copy of the wrapped instance), while passing
object instances will only copy a reference.

In this case, having an additional [weak] reference to our object instance


will not make a difference for the implemented functionality.

So let's compare sizes - this assumes we are storing wrappers in an


array.

Platform Record Object


32-bit 4 4 + 12
64-bit 8 8 + 24

Obviously, records are the clear winner here. Also, using records does
not involve additional heap allocations, reference counting, or taking care
of releasing the wrapper itself.

A number of general coding patterns and practices have been presented


throughout this book along with explanations of certain concepts,
features and functionality. However, there are still some useful coding
patterns related to memory management that either cover more topics at
once or are complex enough to distract from the topic itself, and were not
covered because of this.

Some of the patterns are intended to solve specific manual memory


management issues, while some are universal solutions for problems that
exist in all systems. However, all of the patterns presented are
compatible with all compilers, even though on ARC they either solve a
non-existent problem or add additional reference counting into the
picture, and further optimizations that cater for the specific needs of ARC
compiler could be added.

17.6 Smart Pointers


Manually managing memory is a tedious and error prone job, something
that has to be done but nobody really enjoys doing it. Managing
temporary local objects is the worst. All those try...finally blocks really
hurt code readability. Forget them, and if an exception happens, you can
wind up leaking not just memory, but other resources, too. And if you
have to create more than one local object, creating a nice unreadable
mess seems almost inevitable.

Using auto-managed reference counted classes is one of the solutions


for the above problems, but it does not help much if you deal with regular
classes out of your control. However, we can still use the provided
reference counting to create a lifetime management wrapper class that
can spare us some dirty work each time we need to allocate a new
object.

A similar wrapper feature called a 'smart pointer' exists in C++, wrapping


regular pointers to give them automatic memory management (and some
other features). Since we will be wrapping object references rather than
plain pointers, Smart Reference would probably be a more descriptive
name, but Smart Pointer is a more familiar term so I will use that one.

First we need to create a generic wrapper interface, ISmartPointer<T> , with a


single function returning our wrapped object. Let's call it Value .

Next we need a class implementing that interface. Using reference


counted objects through object references messes up the reference
counting mechanism. Depending on the code, it can result in memory
leaks or premature destruction of the underlying object instance. To be on
the safe side, we can declare the Value function as private. That way we
can only access it through an ISmartPointer interface reference and if we
mistakenly try to access it through a TSmartPointer object reference, we will
get a compile time error.

Our implementing class has two constructors. The first one is for
convenience, and automatically creates the wrapped object instance
using a default constructor. If the wrapped class requires calling a custom
constructor, we can also wrap an existing object instance. We will release
the wrapped object in the destructor, achieving our goal of automatic
destruction of the wrapped object when the reference counted wrapper
instance goes out of scope.

This is the most basic SmartPointer implementation that can easily be


extended with additional functionality if needed:
unit uSmartPtr;

interface

type
ISmartPointer<T> = interface
function Value: T;
end;

TSmartPointer<T: class, constructor> =


class(TInterfacedObject, ISmartPointer<T>)
private
FValue: T;
function Value: T;
public
constructor Create; overload;
constructor Create(AValue: T); overload;
destructor Destroy; override;
end;

implementation

constructor TSmartPointer<T>.Create;
begin
inherited Create;
FValue := T.Create;
end;

constructor TSmartPointer<T>.Create(AValue: T);


begin
inherited Create;
FValue := AValue;
end;

destructor TSmartPointer<T>.Destroy;
begin
FValue.Free;
inherited;
end;

function TSmartPointer<T>.Value: T;
begin
Result := FValue;
end;

end.

Now we can wrap any class we want, TStringList for instance, and use it
the in following manner:
procedure Managed;
var
sl: ISmartPointer<TStringList>;
begin
sl := TSmartPointer<TStringList>.Create();
sl.Value.Add('I am inside automanaged StringList');
end;

replacing the usual try...finally coding pattern.


procedure Unmanaged;
var
sl: TStringList;
begin
sl := TStringList.Create;
try
sl.Add('I am inside regular StringList');
finally
sl.Free;
end;
end;

In our Managed procedure, the wrapped TStringList instance will be destroyed


at the end of the procedure - when sl goes out of scope. If there is a
need to release it at an earlier point, sl := nil; will do the trick.

The wrapped instance can also be passed to other procedures/methods.


Basically, we can use it any way we like. The only thing we have to keep
in mind (just like with regular object references) is that we don't grab and
store the wrapped instance and keep it alive longer than its wrapper, or
we would end up with a dangling object reference.
procedure UseStrings(Strings: TStrings);
begin
writeln(Strings.Count);
end;

procedure Managed;
var
sl: ISmartPointer<TStringList>;
begin
sl := TSmartPointer<TStringList>.Create();
sl.Add('I am inside an automanaged StringList');

UseStrings(sl);
end;

As said earlier, the Value function is private, so using the above


construction will result in a compile time error:

Undeclared identifier: 'Value'

procedure Managed;
var
sl: TSmartPointer<TStringList>;
begin
sl := TSmartPointer<TStringList>.Create();
sl.Value.Add('I am inside an automanaged StringList');
end;
Nice. We got rid of the try...finally blocks, we cannot accidentally leak
memory, the code is shorter and more readable, but on the other hand
we have to use Value whenever we want to access our wrapped object
instance. It hurts readability, and refactoring code from the unmanaged to
the managed variant will require more extensive changes.

17.6.1 Anonymous methods to the rescue.

What if we could get rid of Value ? By exploiting an implementation detail of


anonymous methods, we can do that with ease. We just need to change
a few places in our code. We have to change ISmartPointer 's declaration
and rename our Value function to Invoke .
unit uSmartPtr;

interface

type
ISmartPointer<T> = reference to function: T;

TSmartPointer<T: class, constructor> =


class(TInterfacedObject, ISmartPointer<T>)
private
FValue: T;
function Invoke: T;
public
constructor Create; overload;
constructor Create(AValue: T); overload;
destructor Destroy; override;
function Extract: T;
end;

implementation

constructor TSmartPointer<T>.Create;
begin
inherited Create;
FValue := T.Create;
end;

constructor TSmartPointer<T>.Create(AValue: T);


begin
inherited Create;
FValue := AValue;
end;
destructor TSmartPointer<T>.Destroy;
begin
FValue.Free;
inherited;
end;

function TSmartPointer<T>.Invoke: T;
begin
Result := FValue;
end;

function TSmartPointer<T>.Extract: T;
begin
Result := FValue;
FValue := nil;
end;

end.

Now we can use our Smart Pointer and access a wrapped instance just
like we would use a regular object instance. The only differences are in
the instance's declaration and creation call - quite an acceptable trade-
off.
procedure Smart;
var
sl: ISmartPointer<TStringList>;
begin
sl := TSmartPointer<TStringList>.Create();
sl.Add('I am inside automanaged StringList');
end;

17.6.2 So how does the Smart Pointer magic actually work?

In the second Smart Pointer implementation, ISmartPointer<T> is declared as


an anonymous function (method)
ISmartPointer<T> = reference to function: T;

Anonymous methods in Delphi are backed up by a reference counted


object implementing interface with a method called Invoke . Knowing that,
the above anonymous method declaration is the equivalent of having an
interface with the Invoke function:
ISmartPointer<T> = interface
function Invoke: T;
end;

The difference between the two declarations - at least, the difference we


are interested in here - is that with anonymous functions you don't have
to explicitly call Invoke . The compiler will do that for you.

Since ISmartPointer<T> is an anonymous function, which is actually an


interface in the declaration of the TSmartPointer<T> class, the Invoke method
will be mapped to ISmartPointer<T> .

So when you write sl.Add in the above code, behind the curtains that
translates to a sl.Invoke function call which returns a TStringList instance
from FValue , on which we can use the Add method.

The above implementation has the Extract function that is used for
disassociating the wrapped object from the Smart Pointer without
releasing it. It is used for ownership transfer.

17.6.3 Use cases

Shared instances under manual memory management require some


tracking mechanism that will ensure we don't destroy the object instance
while it is in use. To achieve that in the classic compiler we could use
reference counted classes, or if we have to achieve a shared instance
with a non-counted class, we can do that with the help of the
SmartPointer wrapper.

Just as with any other reference counted object, using a SmartPointer


wrapper comes with some small overhead cost. If you have time-critical
code where every CPU cycle matters, especially multi-threaded code,
sticking to manual memory management might still be a better option.

Another issue with using SmartPointer is that there is no universal


implementation provided by the Delphi RTL. Just like some other
Nullable, Lazy, Weak and similar wrappers, introducing SmartPointer
means introducing a specific coding pattern that is not commonly used.

Another disadvantage of this kind of SmartPointer implementation is that


on the ARC compiler, it introduces another implicit reference since
objects are reference counted there, effectively doubling reference
counting triggers.

17.7 Lazy
Lazy initialization is a common pattern for constructing heavyweight
object instances on demand. Some languages even have such
capabilities built in, and instances are constructed when they are
accessed for the first time during program execution. There is another
pattern often related to lazy initialization, and that is to release such an
object when it is not actively being used at the moment, until the next
time it will be required.

Wrapping up a lazy instance and protecting its access reduces potential


memory management issues or resurrection of the lazy instance during
its owner destruction process as demonstrated in the FreeAndNil chapter.

As we all know, a Death Star is a huge thing. When you are operating
your Imperial Fleet, the last thing you need is something the size of a
small moon, just sitting in your memory. It would be great if you could
construct the Death Star on demand, only when you actually want to blow
up a planet and not sooner. On the other hand, TIE fighters are much
smaller and do not have to be lazy instantiated. They are here to show
the difference in behavior between a regular owned field and a lazy one.

The following example shows a lazy wrapper hardcoded around a


particular class, but it can easily be transformed to a generic one. The
only slightly problematic part of implementing a generic wrapper can be
object instance construction, if the class being instantiated requires a
special constructor.

When the TImperialFleet instance is constructed, it creates all owned fields


at once. Since the lazy wrapper around the Death Star is just a thin shell,
it can be freely constructed in the constructor and destroyed in the
destructor. When the Death Star instance is needed, it can be accessed
through its lazy wrapper - that will take care of constructing it if it is not
already constructed. All the assignment checks and dealing with potential
nil instances are confined within the lazy wrapper, and the rest of the
code does not have to take care of that.

While avoiding accessing nil instances in the TImperialFleet class would not
pose too much of a problem at this point because there is not a lot of
functionality there, as it grows and more code is added, dealing with nil
could become problematic. Using a lazy wrapper is easy to implement
and can save you much grief.
program LazyDeathStar;

{$APPTYPE CONSOLE}

type
TTIEFighter = class(TObject)
public
procedure Fight;
constructor Create;
destructor Destroy; override;
end;

TDeathStar = class(TObject)
public
constructor Create;
destructor Destroy; override;
procedure DestroyPlanet(const Planet: string);
end;

TDeathStarLazyWrapper = class(TObject)
strict private
FInstance: TDeathStar;
function GetIsAssigned: boolean;
function GetInstance: TDeathStar;
public
destructor Destroy; override;
property IsAssigned: boolean read GetIsAssigned;
property Instance: TDeathStar read GetInstance;
end;

TImperialFleet = class(TObject)
strict protected
TIE: TTIEFighter;
DeathStar: TDeathStarLazyWrapper;
public
constructor Create;
destructor Destroy; override;
procedure Fight;
procedure DestroyPlanet(const Planet: string);
end;
constructor TTIEFighter.Create;
begin
inherited;
Writeln('TIE ready');
end;

destructor TTIEFighter.Destroy;
begin
Writeln('TIE destroyed');
inherited;
end;

procedure TTIEFighter.Fight;
begin
Writeln('TIE engaged');
end;

constructor TDeathStar.Create;
begin
inherited;
Writeln('Death Star constructed');
end;

destructor TDeathStar.Destroy;
begin
Writeln('Death Star destroyed');
inherited;
end;

procedure TDeathStar.DestroyPlanet(const Planet: string);


begin
Writeln(Planet, ' has been destroyed!!!');
end;

destructor TDeathStarLazyWrapper.Destroy;
begin
FInstance.Free;
inherited;
end;

function TDeathStarLazyWrapper.GetIsAssigned: boolean;


begin
Result := Assigned(FInstance);
end;

function TDeathStarLazyWrapper.GetInstance: TDeathStar;


begin
if not Assigned(FInstance) then FInstance := TDeathStar.Create;
Result := FInstance;
end;

constructor TImperialFleet.Create;
begin
inherited;
TIE := TTIEFighter.Create;
DeathStar := TDeathStarLazyWrapper.Create;
end;

destructor TImperialFleet.Destroy;
begin
Writeln('Destroying Imperial Fleet');
if DeathStar.IsAssigned then Writeln('Death Star is operational');
TIE.Free;
DeathStar.Free;
inherited;
end;

procedure TImperialFleet.DestroyPlanet(const Planet: string);


begin
DeathStar.Instance.DestroyPlanet(Planet);
end;

procedure TImperialFleet.Fight;
begin
TIE.Fight;
end;

var
Fleet: TImperialFleet;

begin
Fleet := TImperialFleet.Create;
try
Fleet.Fight;
Fleet.Fight;
Fleet.DestroyPlanet('Alderaan');

Fleet.Fight;
finally
Fleet.Free;
end;
end.

TIE ready
TIE engaged
TIE engaged
Death Star constructed
Alderaan has been destroyed!!!
TIE engaged
Destroying Imperial Fleet
Death Star is operational
TIE destroyed
Death Star destroyed

If we remove Fleet.DestroyPlanet('Alderaan') from the above example, the


output will change accordingly, and it will be visible that the Death Star
never came into existence.
TIE ready
TIE engaged
TIE engaged
TIE engaged
Destroying Imperial Fleet
TIE destroyed

The above example still has one drawback. As soon as you need to blow
up a planet, you will get stuck with that thing the size of a small moon in
memory. How about releasing it if you won't need it in the foreseeable
future?

A slight modification to the lazy wrapper will do the trick.


TDeathStarLazyWrapper = class(TObject)
...
procedure Clear;
...
end;

TImperialFleet = class(TObject)
...
procedure DestroyPlanet(const Planet: string);
procedure PackDeathStar;
end;

procedure TDeathStarLazyWrapper.Clear;
begin
FInstance.Free;
FInstance := nil;
end;

procedure TImperialFleet.PackDeathStar;
begin
DeathStar.Clear;
end;
or if you prefer FreeAndNil , now is a good place to use it.
procedure TDeathStarLazyWrapper.Clear;
begin
FreeAndNil(FInstance);
end;

And now the Imperial Fleet can rule the galaxy without worrying it will run
out of space...
var
Fleet: TImperialFleet;
begin
Fleet := TImperialFleet.Create;
try
Fleet.Fight;
Fleet.Fight;
Fleet.DestroyPlanet('Despayre');
Fleet.DestroyPlanet('Alderaan');
Fleet.PackDeathStar;
Fleet.Fight;
Fleet.DestroyPlanet('Yavin IV');
finally
Fleet.Free;
end;

TIE ready
TIE engaged
TIE engaged
Death Star constructed
Despayre has been destroyed!!!
Alderaan has been destroyed!!!
Death Star destroyed
TIE engaged
Death Star constructed
Yavin IV has been destroyed!!!
Destroying Imperial Fleet
Death Star is operational
TIE destroyed
Death Star destroyed

17.8 Weak
Zeroing weak references greatly simplify coding when you need to hold a
reference to some collaborator object but you are not its owner. However,
they are only available in the ARC compiler, and the classic compiler
supports such weak references - declared with the [weak] attribute - only
for interface references, and even then only since Delphi Berlin 10.1.

But that does not mean weak references are out of reach. Delphi
provides all that is needed for a weak reference container implementation
that will do all the hard work and increase code safety when dealing with
non-owned references. Such a container can also be used for storing
weak references in collections.

represents a simple wrapper class that supports storing zeroing


TWeak<T>
weak references to object instances, regardless of whether they are
interface or object references, in classic compiler. To simplify its memory
management, it is implemented as a reference counted class and must
be used only through IWeak<T> interface references.

The IWeak interface and the WeakReferences class should not be used directly,
as they only provide necessary supporting functionality.

In order to zero weak references, you must somehow track object


instances that have weak references attached, and you have to be able
to zero them out at the moment the object is destroyed.

Every object destruction finishes with a call to the TObject.FreeInstance virtual


method. The TVirtualMethodInterceptor class makes intercepting FreeInstance
possible for all object instances that have active zeroing weak references
registered, so that they can be zeroed when the original object instance
is destroyed.

When an instance of the TWeak<T> container is constructed with the object


or interface reference we want to wrap passed as a parameter, the
container will be registered with the WeakReferences class along with the
wrapped reference. When the TWeak<T> container goes out of scope, it will
remove itself from the list of weak references that need tracking.

When the original object gets destroyed, the registered interceptor will
invoke the WeakReferences.RemoveInstance procedure, which will zero out all
existing weak references that point to that object.

Using the TWeak<T> container is simple. The only significant thing to


remember is that it is a reference counted class, and that it must be used
through an interface reference.
var
Weak: IWeak<TObject>;
Obj: TObject;
...

// construct empty wrapper


Weak := TWeak.Create(nil);

// construct wrapper
Weak := TWeak.Create(SomeObjectInstance);

// access wrapped instance


Obj := Weak.Ref;
if Assigned(Obj) then
begin
// use Obj
end;

// clear weak reference


Weak.Clear;

// or destroy the wrapper completely


Weak := nil;

unit ZWeakU;

interface

uses
System.TypInfo,
System.Rtti,
System.Classes,
System.Generics.Collections;

type
// support declarations

PInterface = ^IInterface;

IWeak = interface
procedure Zero;
end;

WeakReferences = class
strict private
class var Interceptors: TObjectDictionary<TClass, TVirtualMethodInterceptor
class var Instances: TObjectDictionary<Pointer, TList<IWeak>>;
class constructor ClassCreate;
class destructor ClassDestroy;
public
class procedure AddInterceptor(const Instance: TObject); static;
class procedure RemoveInstance(const Instance: TObject); static;
class procedure AddReference(const Ref: IWeak; const Instance: TObject);
static;
class procedure RemoveReference(const Ref: IWeak; const Instance: Pointer);
static;
end;

// Zeroing weak reference container

IWeak<T> = interface
procedure Clear;
function GetRef: T;
property Ref: T read GetRef;
end;

TWeak<T> = class(TInterfacedObject, IWeak, IWeak<T>)


protected
FRef: Pointer;
function GetRef: T;
procedure Zero;
public
constructor Create(const Instance: T);
destructor Destroy; override;
procedure Clear;
property Ref: T read GetRef;
end;

implementation

class constructor WeakReferences.ClassCreate;


begin
Interceptors := TObjectDictionary<TClass,
TVirtualMethodInterceptor>.Create([doOwnsValues]);
Instances := TObjectDictionary<Pointer, TList<IWeak>>.Create([doOwnsValues]);
end;

class destructor WeakReferences.ClassDestroy;


begin
Instances.Free;
Interceptors.Free;
end;

class procedure WeakReferences.AddInterceptor(const Instance: TObject);


var
Interceptor: TVirtualMethodInterceptor;
FreeMethod: TRttiMethod;
begin
if not Interceptors.TryGetValue(Instance.ClassType, Interceptor) then
begin
FreeMethod := TRttiContext.Create.GetType(Instance.ClassType)
.GetMethod('FreeInstance');

Interceptor := TVirtualMethodInterceptor.Create(Instance.ClassType);
Interceptor.OnBefore := procedure(
Intercepted: TObject; Method: TRttiMethod; const Args: TArray<TValue>
out DoInvoke: boolean; out Result: TValue)
begin
if Method = FreeMethod then
begin
RemoveInstance(Intercepted);
DoInvoke := true;
end;
end;

Interceptors.Add(Instance.ClassType, Interceptor);
end;
Interceptor.Proxify(Instance);
end;

class procedure WeakReferences.RemoveInstance(const Instance: TObject);


var
Value: TList<IWeak>;
i: integer;
begin
TMonitor.Enter(Instances);
try
if Instances.TryGetValue(Instance, Value) then
begin
for i := 0 to Value.Count - 1 do Value[i].Zero;
Instances.Remove(Instance);
end;
finally
TMonitor.Exit(Instances);
end;
end;

class procedure WeakReferences.AddReference(const Ref: IWeak;


const Instance: TObject);
var
Value: TList<IWeak>;
begin
TMonitor.Enter(Instances);
try
AddInterceptor(Instance);
if not Instances.TryGetValue(Instance, Value) then
begin
Value := TList<IWeak>.Create;
Instances.Add(Instance, Value);
end;
Value.Add(Ref);
finally
TMonitor.Exit(Instances);
end;
end;

class procedure WeakReferences.RemoveReference(const Ref: IWeak;


const Instance: Pointer);
var
Value: TList<IWeak>;
i: integer;
begin
TMonitor.Enter(Instances);
try
if Instances.TryGetValue(Instance, Value) then
begin
i := Value.IndexOf(Ref);
if i >= 0 then Value.Delete(i);
end
finally
TMonitor.Exit(Instances);
end;
end;

constructor TWeak<T>.Create(const Instance: T);


var
Obj: TObject;
Intf: IInterface;
begin
inherited Create;
if PPointer(@Instance)^ <> nil then
case PTypeInfo(TypeInfo(T)).Kind of
tkClass:
begin
FRef := PPointer(@Instance)^;
PObject(@Obj)^ := FRef;
WeakReferences.AddReference(Self, Obj);
end;
tkInterface:
begin
FRef := PPointer(@Instance)^;
Intf := IInterface(FRef);
Obj := TObject(Intf);
WeakReferences.AddReference(Self, Obj);
end;
end;
end;
destructor TWeak<T>.Destroy;
begin
Clear;
inherited;
end;

function TWeak<T>.GetRef: T;
begin
if FRef <> nil then
case PTypeInfo(TypeInfo(T)).Kind of
tkClass: PObject(@Result)^ := FRef;
tkInterface: PInterface(@Result)^ := IInterface(FRef);
else Result := default (T);
end
else Result := default (T);
end;

procedure TWeak<T>.Zero;
begin
FRef := nil;
end;

procedure TWeak<T>.Clear;
var
Tmp: Pointer;
Obj: TObject;
Intf: IInterface;
begin
if FRef = nil then Exit;
Tmp := FRef;
FRef := nil;
case PTypeInfo(TypeInfo(T)).Kind of
tkClass:
begin
PObject(@Obj)^ := Tmp;
WeakReferences.RemoveReference(Self, Obj);
end;
tkInterface:
begin
Intf := IInterface(Tmp);
Obj := TObject(Intf);
WeakReferences.RemoveReference(Self, Obj);
end;
end;
end;

end.
The wizard and his magic wand example from the TComponent
notification chapter is a perfect fit for demonstrating zeroing weak
reference.

The only difference here is that the Delphi streaming system does not
support generic classes, nor interfaces, and it only supports resolving
references to TComponent descendants. Because of this, we cannot publish
the Wand property as-is. That is the drawback of the weak reference
approach. If streaming support is not important, using the weak pattern
makes code cleaner and simpler.
program WeakMagic;

uses
System.SysUtils,
ZWeakU in 'ZWeakU.pas';

type
TMaterial = (Wood, Stone, Metal);

TMagicWand = class(TObject)
public
Material: TMaterial;
procedure Magic;
function MaterialToString: string;
destructor Destroy; override;
end;

TWizard = class(TObject)
strict protected
FWand: IWeak<TMagicWand>;
public
procedure UseWand;
property Wand: IWeak<TMagicWand> read FWand write FWand;
end;

procedure TMagicWand.Magic;
begin
case Material of
Wood : Writeln('Casting magic with wooden wand');
Stone : Writeln('Casting magic with stone wand');
Metal : Writeln('Casting magic with metal wand');
end;
end;

function TMagicWand.MaterialToString: string;


begin
case Material of
Wood : Result := 'wood';
Stone : Result := 'stone';
Metal : Result := 'metal';
end;
end;

destructor TMagicWand.Destroy;
begin
Writeln('Wand has been destroyed: ', MaterialToString);
inherited;
end;

procedure TWizard.UseWand;
begin
if Assigned(FWand) and Assigned(FWand.Ref) then FWand.Ref.Magic
else Writeln('Cannot cast magic without a wand');
end;

var
Wizard: TWizard;
WoodenWand, MetalWand: TMagicWand;

begin
ReportMemoryLeaksOnShutdown := true;

// creating wooden wand


WoodenWand := TMagicWand.Create;
WoodenWand.Material := Wood;

// creating metal wand


MetalWand := TMagicWand.Create;
MetalWand.Material := Metal;

// creating wizard
Wizard := TWizard.Create;

// wizard uses wand before he has one


Wizard.UseWand;

// wizard picks up metal wand and uses it


Wizard.Wand := TWeak<TMagicWand>.Create(MetalWand);
Wizard.UseWand;

// wizard picks up wooden wand and uses it


Wizard.Wand := TWeak<TMagicWand>.Create(WoodenWand);
Wizard.UseWand;

// fire destroys all wooden wands


WoodenWand.Free;

// wizard tries to use wand but he no longer has one


Wizard.UseWand;

// clean up
MetalWand.Free;
Wizard.Free;
end.

Cannot cast magic without a wand


Casting magic with metal wand
Casting magic with wooden wand
Wand has been destroyed: wood
Cannot cast magic without a wand
Wand has been destroyed: metal

Since we don't have any setters implemented for the Wand property - we
don't need any to set a weak variable - our output does not include the
pick-up lines we had in the original TComponent based example.

While we don't actually need setters, they might be useful to simplify


assignment of weak references.
Wizard.Wand := TWeak<TMagicWand>.Create(MetalWand);

The added getter and setter encapsulate construction of the weak


wrapper,
TWizard = class(TObject)
strict protected
FWand: IWeak<TMagicWand>;
function GetWand: TMagicWand;
procedure SetWand(const Value: TMagicWand);
public
procedure UseWand;
property Wand: TMagicWand read GetWand write SetWand;
end;

function TWizard.GetWand: TMagicWand;


begin
if Assigned(FWand) then Result := FWand.Ref
else Result := nil;
end;

procedure TWizard.SetWand(const Value: TMagicWand);


begin
FWand := TWeak<TMagicWand>.Create(Value);
end;

and simplify assignment of the collaborator object:


Wizard.Wand := MetalWand;
References
Delphi Reference

http://docwiki.embarcadero.com/RADStudio/en/Delphi_Reference

Delphi Language Guide

http://docwiki.embarcadero.com/RADStudio/en/Delphi_Language_Guide_Index

Rudy's Delphi Corner - Addressing pointers

http://rvelthuis.de/articles/articles-pointers.html

Barry Kelly - Implementing user-defined copy-on-write data


structures in Delphi

http://blog.barrkel.com/2009/01/implementing-user-defined-copy-on-
write.html

Barry Kelly - Stack OverFlow answer - Which variables are initialized


when in Delphi?

http://stackoverflow.com/a/861178/4267244

Wikipedia - Sir Charles Antony Richard Hoare

https://en.wikipedia.org/wiki/Tony_Hoare

QCon - Tony-Hoare - Null References: The Billion Dollar Mistake

https://www.infoq.com/presentations/Null-References-The-Billion-
Dollar-Mistake-Tony-Hoare

Allen Bauer - A "Nullable" Post

https://community.embarcadero.com/blogs/entry/a-
andquotnullableandquot-post-38869
Barry Kelly - Virtual method interception

http://blog.barrkel.com/2010/09/virtual-method-interception.html

Allen Bauer - Exceptional Safety

https://community.embarcadero.com/blogs/entry/exceptional-safety-
28852

Allen Bauer - How can you raise an "Out of Memory" exception when
you don't have any?

https://community.embarcadero.com/blogs/entry/how-can-you-raise-
an-out-of-memory-exception-when-you-dont-have-any-31362

Barry Kelly - Stack OverFlow answer - Problems passing a pointer to


a constructor as a parameter

https://stackoverflow.com/a/6321723/4267244

Allen Bauer - Class Constructors. Popping the hood.

https://community.embarcadero.com/blogs/entry/class-constructors-
popping-the-hood-38899

Barry Kelly - Stack Overflow answer - How to compare TFunc/TProc


containing function/procedure of object?

https://stackoverflow.com/a/5153933/4267244

Barry Kelly - Stack Overflow answer - Should the compiler hint/warn


when passing object instances directly as const interface
parameters?

https://stackoverflow.com/a/4510268/4267244

Objective-C - Transitioning to ARC Release Notes

https://developer.apple.com/library/content/releasenotes/ObjectiveC/RN-
TransitioningToARC/Introduction/Introduction.html
Danny Thorpe - Stack Overflow answer - Is the compiler treatment of
implicit interface variables documented?

https://stackoverflow.com/a/19914385/4267244

Allen Bauer - Give in to the ARC side

https://community.embarcadero.com/blogs/entry/give-in-to-the-arc-
side-38948

Barry Kelly - Anonymous method details

http://blog.barrkel.com/2008/07/anonymous-method-details.html
Quality Portal Reports
RSP-10100 Memory Leak on Const-Parameter

https://quality.embarcadero.com/browse/RSP-10100

RSP-14681 DisposeOf does not decrease reference count on the


calling reference

https://quality.embarcadero.com/browse/RSP-14681

RSP-14682 DisposeOf does not clean up an instance's inner managed-


type references

https://quality.embarcadero.com/browse/RSP-14682

RSP-19204 Compiler turns weak reference captured by anonymous


method into strong one

https://quality.embarcadero.com/browse/RSP-19204

You might also like