5555
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] = NULL
And 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.
 
 
Hosted by uCoz