http://docwiki.embarcadero.com/RADStudio/Seattle/en/Automatic_Reference_Counting_in_Delphi_Mobile_Compilers#Weak_References
Weak References
Another important concept for ARC is the role of weak references, which you can create by tagging them with [weak]
attribute. Suppose that two objects refer to each other using a field, and an external variable refers to the first. The reference count of the first object will be 2 (the external variable, and the second object), while the reference count of the second object is 1. Now, as the external variable goes out of scope, the two objects‘ reference count remains 1, and they remain in memory indefinitely.
To solve this type of situation, and many similar scenarios, is to use a weak reference to break the circular references when the last external reference goes out of scope.
A weak reference is a reference to an object that does not increase its reference count. To declare a weak reference, use the [weak]
attribute, supported by the Delphi mobile compilers.
Given the previous scenario, if the reference from the second object back to the first one is weak, as the external variable goes out of scope, both objects are destroyed. Here is an example of this situation:
type TMyComplexClass = class; TMySimpleClass = class private [Weak] FOwnedBy: TMyComplexClass; public constructor Create(); destructor Destroy (); override; procedure DoSomething(bRaise: Boolean = False); end; TMyComplexClass = class private fSimple: TMySimpleClass; public constructor Create(); destructor Destroy (); override; class procedure CreateOnly; end;
In the following code snippet, the constructor of the complex class creates an object of the other class:
constructor TMyComplexClass.Create; begin inherited Create; FSimple := TMySimpleClass.Create; FSimple.FOwnedBy := self; end;
Remember that the FOwnedBy
field is a weak reference, so it does not increase the reference count of the object it refers to, in this case the current object (self).
Given this class structure, we can write:
class procedure TMyComplexClass.CreateOnly; var MyComplex: TMyComplexClass; begin MyComplex := TMyComplexClass.Create; MyComplex.fSimple.DoSomething; end;
This causes no memory leak, given that the weak reference is properly used.
As a further example of the use of weak references, note this code snippet in the Delphi RTL, part of the TComponent class declaration:
type TComponent = class(TPersistent, IInterface, IInterfaceComponentReference) private [Weak] FOwner: TComponent;
If you use the weak attribute in code compiled by one of the desktop Delphi compilers, the attribute is ignored. Using DCC32, you have to make sure that you add the proper code in the destructor of an "owner" object to also free the "owned" object. Calling Free
is allowed, although the effect is different in the Delphi mobile compilers. The behavior is correct in both in most circumstances.
Note: When an instance has its memory released, all active [weak] references are set to nil. Just as a strong (normal) reference, a [weak] variable can only be nil or reference a valid instance. This is the main reason why a weak reference should be assigned to a strong reference before being tested for nil and dereferenced. Assigning to a strong reference does not allow the instance to be released prematurely.
var O: TComponent;// a strong reference begin O := FOwner; // assigning a weak reference to a strong reference if O <> nil then O.<method>;// safe to dereference end;
Note: You can only pass a [Weak] variable to a var or out parameter that is also marked as [Weak]. You cannot pass a regular strong reference to a [Weak] var or out parameter.