This is not that common of an issue, but it happens – when searching for memory leaks, remember to check any cross-referenced objects. Cross-referenced objects are, well, just that, objects that need to reference each other. For example, when “ClassA” needs to know about “ClassB”, and “ClassB” needs to know about “ClassA”. Continue reading to see how this can cause memory leaks in your projects and how to use Xojo’s WeakRef to fix it.
If you’re dealing with a situation in your code where two classes need to reference each other, then you could be leaking memory if you haven’t taken steps to prevent that.
Let’s look at this with a simple example.
- Create a new Xojo project and add two new classes to it, ClassA and ClassB.
- Add a ClassBReference As ClassB property to ClassA.
- Add a ClassAReference As ClassA property to ClassB.
- Add a Constructor to ClassB using the following signature:
Constructor(reference As ClassA)
And add this line of code to the method:
Me.ClassAReference = reference
Next, add the Destructor method to both classes and type “Break” as the only line of code for them. This will allow us to see if they leak memory or not. For example, if when debugging the example project the Debugger doesn’t reach the Break lines, that would mean that the Destructor has not been called and, thus, the objects are not released from memory. They are leaking!
- Now add the Opening event handler to Window1 and type these lines into it:
CreateReferences
Break
As you can see, it calls the CreateReferences method and then breaks into the debugger.
- Now add the CreateReferences method to Window1 and type these lines of code in the associated Code Editor:
Var ca As New ClassA
Var cb As New ClassB(ca)
ca.ClassBReference = cb
Now it’s clear what is going on here: ClassB instance is keeping a “hard” reference to ClassA (via the ca instance), and then the ca reference is keeping a “hard” reference to the classB instance cb.
Run the project. Is the debugger stopping in the Break lines of ClassA and ClassB Destructors? Nope. They are leaking because they are using hard cross-references with each other.
You can see it more clearly when accessing Global > Runtime > Contents in the Debugger panel. Both objects are still alive:
WeakRef to the Rescue!
But let’s say that your app design needs to keep these references. How do we solve this situation? The answer is using weak references instead of hard references, and the Xojo language has a class for it: WeakRef.
Let’s change ClassA and ClassB properties so their type is a WeakRef. Setting their definition as:
ClassBReference As WeakRef
ClassAReference As WeakRef
And let’s change the ClassB Constructor code so it creates a new WeakRef instance from the received object, instead of assigning it directly to the property.
ClassAReference = New WeakRef(reference)
And let’s change the code in the CreateReferences method to:
Var ca As New ClassA
Var cb As New Classb(ca)
ca.ClassBReference = New WeakRef(cb)
Run the example project again. Now you will see that the Debugger stops at the “Break” lines for each Class Destructor. Which means they are correctly released from memory. Problem solved!
WeakRef Memory Care
When using WeakRef objects through the code, keep in mind that accessing the real object they are referencing is done through the WeakRef.Value property in addition to Cast to the original Class.
To see it more clearly, add the “SayHello” method to ClassB and type this line of code into the associated code editor:
MessageBox("Hi From ClassB Instance")
Then, select the CreateReferences method from Window1 and add the following line of code at the end:
ClassB(ca.ClassBReference.Value).SayHello
First, we are accessing the ClassBRefence property from the ca instance; that gives us access to the WeakRef instance property, so we need to access its Value property in order to get access to the real object instance we are really interested (a ClassB instance).
But, because the Value property returns a generic Object, we need to Cast it to the class instance we expect (and should know) that is referenced by the Value property; in this case a “ClassB” instance. To do this, we are using the Cast expression:
ClassB(ca.ClassBReference.Value)
Next, and because we did Cast it to ClassB, we can continue using the dot notation in order to call any of the methods / properties exposed by ClassB, in this case the “SayHello” method.
Run the app again and now the ClassB instance referenced by the ClassA instance is displaying the MessageBox.
In addition, when accessing a hard referenced object in your class properties it can be good to do a sanity check like:
If ca.ClassBReference <> Nil Then
ca.ClassBReference.SayHello
End If
But when using WeakRefs you need to do a double check. First against the WeakRef itself, because it can be a Nil object! And second, against the object referenced by the WeakRef itself. So the previous check will read now:
If ca.ClassBReference <> Nil And ca.ClassBReference.Value <> Nil then
ClassB(ClassBReference.Value).SayHello
End If
Clearing It!
All in all, cross-referencing objects among class instances is probably not the best design, but sometimes it is needed. In case your app design needs to follow such a path, take care and use weak references (with Xojo’s WeakRef class) instead of hard references.
Keep it in memory (or not, if it is leaking)… and happy coding with Xojo!
Javier Menendez is an engineer at Xojo and has been using Xojo since 1998. He lives in Castellón, Spain and hosts regular Xojo hangouts en español. Ask Javier questions on Twitter at @XojoES or on the Xojo Forum.