When a node allocates memory on the heap and sends the pointer through an output port, the pointer travels to other nodes. The other nodes can hold onto that pointer for any amount of time. There's no guarantee of which node will be the last to use the pointer. Who owns that heap-allocated data? Who is responsible for deallocating it?
The answer is that the Vuo runtime takes ownership of the data as soon as it leaves the node, and is responsible for deallocating it. The Vuo runtime uses reference counting to keep track of heap-allocated memory and deallocate it when all nodes are finished using it.
You need to use Vuo's reference-counting system to manage memory if:
Read on to learn how.
Vuo provides four functions for reference-counting:
When compiling a composition, the Vuo compiler automatically inserts most of the necessary calls to VuoRegister(), VuoRetain(), and VuoRelease(). If you're implementing a stateless node class, you never need to call these functions. If you're implementing a stateful node class or a port type, read on to learn when to call these functions.
In many cases, you can rely on the port types and the Vuo compiler to handle all the memory management.
By convention, a port type function that constructs port data is responsible for registering that data. For example, when you call VuoText_make(), it allocates memory for the VuoText value and calls VuoRegister() on it.
The Vuo compiler automatically inserts calls to VuoRetain() and VuoRelease() based on the type of the port or node instance data. For port data, "type" means its underlying data type, which is
typedefed in the port type's header file. For node instance data, "type" means the type returned by nodeInstanceInit().
If the data type is a pointer, then VuoRetain() or VuoRelease() is called on the data itself. If the data type is a struct, then VuoRetain() or VuoRelease() is called on each field of the struct that's a pointer, each field of any struct inside the struct that's a pointer, and so on (a recursive traversal).
In the example below, VuoText_make() allocates a new VuoText value, copies
textAsCString into it, registers it, and returns it. The nodeEvent() function sends this VuoText value through the output port. After the nodeEvent() function returns, the Vuo compiler handles all calls to VuoRetain() and VuoRelease() for the VuoText value. (However, you do have to free
textAsCString, since it's just a temporary value that never leaves the node.)
The example below is almost the same, except that the output port is a trigger port.
If you're defining a port type that's a pointer to heap-allocated data, then your port type is responsible for registering that data. For example:
If you're defining a port type that's a struct, and the struct contains pointers to heap-allocated data, then your port type is responsible for registering that data. For example:
If you're defining a port type that's some kind of container than a struct, and it contains pointers to heap-allocated data, then you have to do a little more work. Unlike a struct, the Vuo compiler can't analyze your container to automatically insert calls to VuoRetain() and VuoRelease(). Your port type is responsible for registering, retaining, and releasing the items in the container, as well as registering the container itself. For example:
If instead of VuoText this container held a struct port type such as VuoSceneObject, then you would replace each call to VuoRetain() or VuoRelease() with a call to MyType_retain() or MyType_release(). In fact, you can call MyType_retain() or MyType_release() for any type. These functions are automatically generated by the Vuo compiler (though need to be declared in
MyType.h). Depending on the underlying data type of
MyType, they call VuoRetain() and VuoRelease() as needed.
If the instance data is a container for heap-allocated items, then it needs to retain items that it stores and and release items that it no longer stores. Use VuoRetain() and VuoRelease(), just like in the example above for the opaque container port type.
|Functions to manage memory for heap-allocated port and node instance data. |