The XComponent Interface
A central problem of reference counting systems is cyclic references. Assume Object A keeps a reference on object B and B keeps a direct or indirect reference on object A. Even if all the external references to A and B are released, the objects are not destroyed, which results in a resource leak.
In UNO, the developer must explicitly decide when to the break cyclic references. To support this concept, the interface com.sun.star.lang.XComponent exists. When an XComponent
is disposed of, it can inform other objects that have expressed interest to be notified.
// within the module com::sun::star::lang // when dispose() is called, previously added XEventListeners are notified interface XComponent: com::sun::star::uno::XInterface { void dispose(); void addEventListener( [in] XEventListener xListener ); void removeEventListener( [in] XEventListener aListener ); }; // An XEventListener is notified by calling its disposing() method interface XEventListener: com::sun::star::uno::XInterface { void disposing( [in] com::sun::star::lang::EventObject Source ); };
Other objects can add themselves as com.sun.star.lang.XEventListener to an XComponent
. When the dispose()
method is called, the object notifies all XEventListeners
through the disposing()
method and releases all interface references, thus breaking the cyclic reference.
A disposed object is unable to comply with its specification, so it is necessary to ensure that an object is not disposed of before calling it. UNO uses an owner/userconcept for this purpose. Only the owner of an object is allowed to call dispose and there can only be one owner per object. The owner is always free to dispose of the object. The user of an object knows that the object may be disposed of at anytime. The user adds an event listener to discover when an object is being disposed. When the user is notified, the user releases the interface reference to the object. In this case, the user should not call removeEventListener()
, because the disposed object releases the reference to the user.
This solves the problem described above. However, there are a few conditions which still have to be met.
If an object is called while it is disposed of, it should behave passively. For instance, if removeListener()
is called, the call should be ignored. If methods are called while the object is no longer able to comply with its interface specification, it should throw a com.sun.star.lang.DisposedException, derived from com.sun.star.uno.RuntimeException. This is one of the rare situations in which an implementation should throw a RuntimeException
. The situation described above can always occur in a multithreaded environment, even if the caller has added an event listener to avoid calling objects which were disposed of by the owner.
The owner/user concept may not always be appropriate, especially when there is more than one possible owner. In these cases, there should be no owner but only users. In a multithreaded scenario, dispose()
might be called several times. The implementation of an object should be able to cope with such a situation.
The XComponent
implementation should always notify the disposing()
listeners that the object is being destroyed, not only when dispose()
is called, but when the object is deleted. When the object is deleted, the reference count of the object drops to zero. This may happen when the listeners do not hold a reference on the broadcaster object.
The XComponent
does not have to be implemented when there is only one owner and no further users.
Content on this page is licensed under the Public Documentation License (PDL). |