![]() |
![]() |
The cross references problem. How resolve it? |
> I have two objects, each of which refers to the
another.
> At their destruction they remain in memory. Whether there is a way of their
correct removal?
> Here a simple example:
PUBLIC goObj1, goObj2
goObj1 = NewObject("MyCls1")
goObj2 = NewObject("MyCls2")
goObj1.oProp2 = goObj2
goObj2.oProp1 = goObj1
DO cleanup
DEFINE CLASS MyCls1 As Session
oProp2 = NULL
PROCEDURE Destroy
this.oProp2 = NULL
ENDPROC
ENDDEFINE
DEFINE CLASS MyCls2 As Session
oProp1 = NULL
PROCEDURE Destroy
this.oProp1 = NULL
ENDPROC
ENDDEFINE
PROCEDURE cleanup
IF Type('goObj1') = 'O' AND !IsNull(goObj1)
RELEASE goObj1
ENDIF
IF Type('goObj2') = 'O' AND !IsNull(goObj2)
RELEASE goObj2
ENDIF
> If look trace the work of example mentioned above
under debugger,
> can easily be seen, that the events Destroy() at both classes do not
fulfilment :-(
Yes, it's true. The problem that event Destroy() can not be executed because
there is a property-reference on the existing object... and to get rid, it is
necessary execute its destruction before event Destroy() object. For example:
PUBLIC goObj1, goObj2
goObj1 = NewObject("MyCls1")
goObj2 = NewObject("MyCls2")
goObj1.oProp2 = goObj2
goObj2.oProp1 = goObj1
DO cleanup
DEFINE CLASS MyCls1 As Session
oProp2 = NULL
PROCEDURE RemoveAllRefs
this.oProp2 = NULL
ENDPROC
PROCEDURE Destroy
this.RemoveAllRefs() && Let's hope that in the future versions it will be was correctly :-)
ENDPROC
ENDDEFINE
DEFINE CLASS MyCls2 As Session
oProp1 = NULL
PROCEDURE RemoveAllRefs
this.oProp1 = NULL
ENDPROC
PROCEDURE Destroy
this.RemoveAllRefs()
ENDPROC
ENDDEFINE
PROCEDURE cleanup
IF Type('goObj1') = 'O' AND !IsNull(goObj1)
goObj1.RemoveAllRefs()
RELEASE goObj1
ENDIF
IF Type('goObj2') = 'O' AND !IsNull(goObj2)
goObj2.RemoveAllRefs()
RELEASE goObj2
ENDIF
Below similar example for classes, derivatived from Form
#DEFINE USE_RELEASE .F.
ACTIVATE SCREEN
CLEAR
PUBLIC goObj1, goObj2
goObj1 = NewObject("MyCls1")
goObj2 = NewObject("MyCls2")
goObj1.oProp2 = goObj2
goObj2.oProp1 = goObj1
DO cleanup
DEFINE CLASS MyCls1 As Form
Name = "MyCls1"
oProp2 = NULL
PROCEDURE Destroy
ACTIVATE SCREEN
? "Destroy: " + this.Name
this.oProp2 = NULL
ENDPROC
ENDDEFINE
DEFINE CLASS MyCls2 As Form
Name = "MyCls2"
oProp1 = NULL
PROCEDURE Destroy
ACTIVATE SCREEN
? "Destroy: " + this.Name
this.oProp1 = NULL
ENDPROC
ENDDEFINE
PROCEDURE cleanup
ACTIVATE SCREEN
? "Call Cleanup"
IF Type('goObj1') = 'O' AND !IsNull(goObj1)
#IF USE_RELEASE
goObj1.Release()
#ENDIF
RELEASE goObj1
ENDIF
IF Type('goObj2') = 'O' AND !IsNull(goObj2)
#IF USE_RELEASE
goObj2.Release()
#ENDIF
RELEASE goObj2
ENDIF
Pay attention, that an explicit call of the method Release() at the forms (with USE_RELEASE .T.), solve a problem. In the sense that compels to be executed event Destroy() instance of object.
Is noticed also, that if you keep the references in the array, for correct removal of the appropriate objects the obvious assignment NULL as value to the appropriate element of array, i.e. it's necessary
gaMyObjects[i] = NULLAnd because of told, above method Release() at classes derivative from Form, better so:
LOCAL luTemp
...
luTemp = gaMyObjects[i]
IF TYPE('luTemp') = 'O' AND !ISNULL(luTemp)
IF INLIST(luTemp.BaseClass, "Form", "FormSet", "ToolBar")
*-- Insert any special-destroy actions here...
luTemp.Release()
ENDIF
ENDIF
STORE NULL TO ;
luTemp, gaMyObjects[i]
...
However sometimes, the explicit call of the method Release() of the form can and
not result in its destruction. It can happen for example, that the reference to
one of objects belonging to the form, is used somewhere in outside of the this
form.
*//////////////////////////////////// save as any.prg
*// Demo example problems
*// with cross-refs in forms
*// VFP ver: 8.0(SP1VFP8), 7.0(SP1VFP7), 6.0(SP5VS6)
*//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*// Author: Michael Drozdov
*// My Page: http://vfpdev.narod.ru/
*// Date: 11/12/2003
*//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
*-- If USE_CLEARPROPS is true (USE_REMOVECHILDOBJS .F.)
*-- works correctly, only in case clear all external references
*-- by explicit use command: RELEASE goMyAnyFrm or method: goMyAnyFrm.Release()
#DEFINE USE_CLEARPROPS .F.
*
*-- If USE_REMOVECHILDOBJS is true
*-- all works correctly, ...and there are problems otherwise
#DEFINE USE_REMOVECHILDOBJS .F.
*
*-- For explicit use command: RELEASE goMyAnyFrm
#DEFINE USE_CLEANUP .F.
*
*-- (if USE_CLEANUP .T.) For explicit use method: goMyAnyFrm.Release()
#DEFINE USE_RELEASE .F.
PUBLIC goFrm1
ACTIVATE SCREEN
CLEAR
*-- Create Form1
goFrm1 = NEWOBJECT('Frm1')
IF VARTYPE(goFrm1) # 'O'
DO cleanup
RETURN .F.
ENDIF
goFrm1.Show()
*-- Create Form2
PUBLIC goFrm2
goFrm2 = NEWOBJECT('Frm2')
IF VARTYPE(goFrm2) # 'O'
DO cleanup
RETURN .F.
ENDIF
goFrm2.Show()
*-- Init cross references
*-- between child objects of the forms
goFrm1.oCmdExit2 = goFrm2.cmdExit
goFrm2.oCmdExit1 = goFrm1.cmdExit
#IF USE_CLEANUP
DO cleanup
#ENDIF
DEFINE CLASS Frm1 As Form
Caption = "Form1"
oCmdExit2 = NULL
Left = 120
ADD OBJECT cmdExit As CommandButton WITH ;
Caption = "Cancel",;
Top = 5
PROCEDURE cmdExit.Click()
ThisForm.Release()
ENDPROC
PROCEDURE Destroy()
ACTIVATE SCREEN
?"Destroy: "+ThisForm.Caption
#IF USE_CLEARPROPS
*-- Release property to external ref
this.oCmdExit2 = NULL
#ENDIF
#IF USE_REMOVECHILDOBJS
*-- Release object, the reference on which
*-- can be used by external objects
this.RemoveObject('cmdExit')
#ENDIF
ENDPROC
ENDDEFINE
DEFINE CLASS Frm2 As Form
Caption = "Form2"
AutoCenter = .T.
oCmdExit1 = NULL
ADD OBJECT cmdExit As CommandButton WITH ;
Caption = "Cancel",;
Top = 5
PROCEDURE cmdExit.Click()
ThisForm.Release()
ENDPROC
PROCEDURE Destroy()
ACTIVATE SCREEN
?"Destroy: "+ThisForm.Caption
#IF USE_CLEARPROPS
*-- Release property to external ref
this.oCmdExit1 = NULL
#ENDIF
#IF USE_REMOVECHILDOBJS
*-- Release object, the reference on which
*-- can be used by external objects
this.RemoveObject('cmdExit')
#ENDIF
ENDPROC
ENDDEFINE
PROCEDURE cleanup
ACTIVATE SCREEN
?"Call Cleanup"
IF Type('goFrm1') = 'O' AND !IsNull(goFrm1)
#IF USE_RELEASE
goFrm1.Release()
#ENDIF
RELEASE goFrm1
ENDIF
IF Type('goFrm2') = 'O' AND !IsNull(goFrm2)
#IF USE_RELEASE
goFrm2.Release()
#ENDIF
RELEASE goFrm2
ENDIF
*/////////////////////////////// the end of any.prg
The most radical way consists of use explicit removals of child objects by use
method RemoveObject() class-container (USE_REMOVECHILDOBJS .T.). However, it is
not necessary to resort to it without emergency.