Reference System Best Practices
The following is a list of best practices regarding usage of the reference system:
Make sure you choose correctly between storing a pointer or a reference in your plug-in to a scene entity. Storing a pointer to a scene entity is seldom safe because the pointer could become invalid if the scene entity is deleted and your plug-in fails to update the pointer. Although pointers to scene entities could be used as local variables in plug-in member functions, they should not be cached as plug-in data members and their life span should be as short as possible.
For information on converting pointers to weak or indirect references see Using Indirect References Instead of Pointers.
References can be stored as plug-in member variables but it is the plug-in's responsibility to properly implement their management, so that they do get updated, do not prevent the dependent entity to be destroyed, etc.
Be very careful if holding a pointer to a scene entity in a local variable when making system calls that may cause that scene entity to be deleted. Do not rely on Restore Objects being created to prevent scene entity deletion as, particularly when dealing with MAXScript, the hold system may be suspended. In such cases, instead of directly holding a pointer you should hold the pointer via a SingleRefMaker, SingleWeakRefMaker, TypedSingleRefMaker, or TypedSingleWeakRefMaker.
- When observing nodes always use weak references instead of strong references. This is because nodes can be deleted by the user.
- Make sure you choose the right type of reference your plug-in needs to create to a scene entity it depends on. See the topic Kinds of References for more information.
- Never store pointers to scene nodes as plug-in data members because they can be invalidated at any time (e.g. due to the user deleting them). Use a node monitor instead (INodeMonitor or INodeTransformMonitor).
- A plug-in's implementation of ReferenceMaker::SetReference() should always assign the ReferenceTarget passed in by the caller to the corresponding plug-in data member, as opposed to implementing its own logic to update that reference.
- A plug-in that needs to maintain a variable number of references, should use the class IRefTargContainer.
- In order to allow 3ds Max to manage the life-time of your plug-in instances, the plug-in would not override Animatable::DeleteThis() or ReferenceTarget::AutoDelete(). When a plug-in needs to behave as a singleton, it should overwrite ReferenceTarget::AutoDelete() to return REF_SUCCEED. In this case, the plug-in should take care of deleting itself its sole instance since 3ds Max will not be able to do it.
- Do not call the delete operator on a pointer to an object derived from Animatable or any of its sub-classes. For more information see Reference Object Lifetime Management.
- For managing one single reference, a plug-in should consider using an instance of class SingleRefMaker or class SingleWeakRefMaker.
- Initialize ReferenceTarget pointers to NULL before the first call to ReferenceMaker::ReplaceReference(). This should be done in the member initialization list of the constructor.
- When receiving a REFMSG_TARGET_DELETED message a ReferenceMaker should always set its reference to the ReferenceTarget instance being deleted to NULL.
- If the plug-in wishes to delete itself in response to REFMSG_TARGET_DELETED, it should return REF_AUTO_DELETE from its implementation of ReferenceMaker::NotifyRefChanged().
- Never call ReferenceMaker::SetReference(), always call ReferenceMaker::ReplaceReference() instead.
- Use Animatable::GetAnimByHandle() and compare the result to NULL to verify that an animatable (or ReferenceTarget) pointer is valid.
- If appropriate call ReferenceMaker::DeleteAllRefs() in the destructor of a plug-in that derives from reference maker, to assure that all references are deleted. Normally if an object is deleted correctly then this shouldn't be necessary.
- Send reference messages sparingly. They can slow down the system. For example if a large number of changes are made to a plug-in at once, then the plug-in should try to send only a single REFMSG_CHANGE message for the set of changes.
- When setting ReferenceTarget pointers to NULL in repsonse to REFMSG_TARGET_DELETED, be sure to only assign one pointer to the ReferenceTarget to NULL in the case that you have multiple references to the same target.
A reference maker should support the setting of NULL references for any of its reference indexes.