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 ENDIFBelow 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.prgThe 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.