We all know the problem that object cannot refer to each other without creating the dreaded circular references that prevent automatic destruction, unless the objects in question are in a parent/child relationship and the references are the builtin ones (.Parent on one end, .Controls[] on the other).
Sometimes an object needs to hand out other objects representing resources owned by it in some fashion but needs to keep references to everything it hands out to allow forceful removal. .AddObject does most of the job but not all - the objects handed out are not cleaned up up automatically because they are still referenced by the owning container.
Enter the 'handle' class. The resource will be wrapped in a normal class which contains a property reference to a handle object which is actually a child of the container, created via .AddObject with a pseudo-random name. It is the job of the handle object to manage the resource in collusion with the container (which it can access via its .Parent property) and to relay news of the resource object's destruction to the container to allow cleanup of the resource.
The resource objects themselves follow normal lifetime rules and they will be cleaned up normally, because the resource owner does not reference them. These objects will even survive destruction of the resource container; all that happens is that the handle property turns .null.
The handle object could forward certain requests to the container and it could also store some information (like operating system handles). But in the simplest case it needs to do nothing else because even as it is it can prevent the resource object from doing certain things that it should not do when the resource owner/container has gone away.
Note: the resource owner/container will not get destroyed automatically until all references to it are released and all resource objects that it handed out have gone away. For some purposes this is just fine as it is, but it can also forcefully destroy all outstanding handles if it so chooses.
return createobject([CResourceDispenser])
#define CHandle_ CResourceDispenser_Handle_6KV5YtzcwCEd1oN8cUhxo_
define class CResourceDispenser as custom
function CreateResource
this.AddObject("handle" + sys(2015), [CHandle_])
return createobject([CResource], this.Controls[this.ControlCount])
procedure FreeResourceByHandle (oHandle)
this.RemoveObject(m.oHandle.Name)
procedure FreeAllResources
do while this.ControlCount > 0
this.RemoveObject(this.Controls[this.ControlCount].Name)
enddo
enddefine
define class CHandle_ as custom
procedure Suicide
this.Parent.FreeResourceByHandle(this)
enddefine
define class CResource as relation
o_Handle = .null.
function Init (oHandle)
if isnull(m.oHandle)
return .f.
endif
this.o_Handle = m.oHandle
procedure Destroy
if not isnull(this.o_Handle)
this.o_Handle.Suicide
endif
procedure o_Handle_ASSIGN (uNewValue)
if isnull(m.uNewValue) and not isnull(this.o_Handle)
this.Event_HandleDestroyed
endif
this.o_Handle = m.uNewValue
procedure Event_HandleDestroyed
enddefine