NoesisGUI

Reference Counting

Reference counting is a technique of storing the number of references to a resource such as an object or block of memory. It is typically used as a means of deallocating objects which are no longer referenced. It is the simplest implementation of Garbage Collection. Each object contains a count of the number of pointers referring to it, held by other objects. If an object's reference count reaches zero the object is no longer referenced and it can be safely destroyed.

Components in NoesisEngine inherit from BaseRefCounted (indirectly through BaseComponent), the base class for objects with reference counting. This means that all Noesis Components are reference-counted. The first time a component is created, its reference counter is initialized to one.

// Box inherits from BaseComponent
// ref = 1 - created
Box* box = new Box();

//...

// The component is eliminated here
// ref = 0 - destroyed
box->Release();

As shown in the example, components must not be deleted using the operator delete. Invoking the operator delete in a component destroys it automatically ignoring other possible references. The implementation of BaseRefCounted can detect this situacion in Debug asserting whenever it happens.

Due to the same reason, components must not be created in the stack because they are automatically destroyed when they go out of the scope creating possible dangling pointers from other existing references.

References to components can be manually handled using AddReference() / Release(). For example:

Box* box0 = new Box();

Box* box1 = box0;
box0->AddReference();

//...

box0->Release();
box1->Release();

To avoid manually handling the reference counter of the component, they are usually stored inside a Ptr smart pointer. Through the use of Ptr, whenever a reference is destroyed or overwritten, the reference count of the object it references is decremented, and whenever one is created or copied, the reference count of the object it references is incremented.

Ptr<Box> box0 = *new Box();
Ptr<Box> box1 = box0;

//...

// box0 and box1 are automatically destroyed when they go out of scope

As you can see in the example above, to assign a newly created component to a Ptr we use a special constructor (that internally does not increment reference counting) that receives a reference to the instance, because components are always created with 1 reference. That way, when Ptr variable goes out of scope, component reference is released and object is correctly deleted.

Weak references

One of the problems of reference counting is that cycles are not allowed. A cycle of hard references can not be automatically detected and generates leaks. To avoid cycles you should use a raw pointer. A raw pointer does not increase the reference counter. Most of the cycle scenarios can be solved using a raw pointer. The problem with raw pointers is that they become enventually dangling pointers whenever the target instance is destroyed. If you have a scenario where this cannot be easily detected Noesis offers a mechanism that is an improvement to raw pointers: weak pointers. A weak pointer behaves exactly like a raw pointer but whenever the target object is destroyed the weak pointer becomes zero. For example:

WeakPtr<Sphere> w0;

{
    Ptr<Sphere> sphere = *new Sphere(20.0f);
    WeakPtr<Sphere> w0 = sphere;

    NS_UNITTEST_CHECK(w0.Lock() != 0)
}

NS_UNITTEST_CHECK(w0.Lock() == 0)

To use a weak ptr you must use the Lock() that temporarily increases the reference counter of the target. Whenever you finish using the target it is automatically eliminated. This ensures that the target object is not destroyed while being used.

© 2017 Noesis Technologies