Local object event handler removal

In a method of a module, I create a local URLConnection object, add handlers for some of its events, then call its async Send method.

dim Downloader as new URLConnection
AddHandler Downloader.error, AddressOf DownloadError
AddHandler Downloader.FileReceived, AddressOf DownloadFileReceived
Downloader.Send("GET",URL, DownLoadFile,15)

When is the URLConnection object destroyed and the handlers removed? After Send terminates in some event?

Do I need to be concerned about handler removal? WeakAddressOf can’t be used, and handlers are added to a new URLConnection instance every time the method is called.

If the DownloadError and DownloadFileReceived methods are also module methods then the URLConnection is destroyed when the Downloader local variable goes out of scope.

It’s when the handler method belongs to another object that you have to be concerned about creating zombies with AddHandler.

1 Like

When the “Downloader” object goes out of scope, its handlers, if any, should be auto detached by the super destructor of the URLConnection Class (not user’s) and everything should be auto released after.

Yes, those methods are in the same module. The call to Send is the last statement in the method in which Downloader is instantiated, so technically it goes “out of scope”, as that method terminates and execution returns to the main event loop, but the Downloader object still exists somewhere, running asynchronously. I guess it self-destructs after calling one of its terminating events like FileReceived or Error, right?

Maybe there’s a instance manager for this kind of class that keeps them around while there’s an active connection. The other option would be just the object super destructor just drop the connection before continuing its release, or just there’s a bug and it’s not being released in some cases. Someone at Xojo should explain the internals as the docs are obscure.

The URLConnection may continue to exist if it is still connected when it goes out of scope. The old HTTPSocket worked that way.

1 Like

Any time you use AddHandler, you must have a corresponding RemoveHandler call. Keep in mind that you cannot use the Destructor for this because the destructor will not fire if an AddHandler is in place.

Don’t added handlers get cleaned up when a thread or indeed the whole app exits?

When the app exits yes, but not Threads. Threads will leak just like everything else.

Ah. Then I have some work to do. Can a thread remove its own run handler?

If you only need to call run once, you can certainly call RemoveHandler from within the method that was called, using the object that was passed. If not, I suggest that you make a method designed for cleaning up.

If you want more control, use a Delegate and a property that uses that delegate type. Then it’s as simple as setting the property to nil.

Yes, I have a cleaning-up method. I can add all the removehandlers there. Thanks.

Where would I do this in my application? In the delegate methods themselves, i.e. the event handlers for URLConnection.Error and URLConnection.FileReceived?

No other place in the code can possibly know what the status of the URL connection object is, as the method that instantiated it has terminated and the object is technically “out of scope”, as previously discussed.

Now this gets a bit hazy. Do you mean that instead of doing AddHandler AddressOf, and then later doing RemoveHandler AddressOf, I can store the address of the method in a delegate and use the delegate in the add/remove handler statements? That seems to work, but I can’t see where setting it to Nil comes in.

RemoveHandler requires as an argument the name of the event, e.g. “MyTimer.Action”, but in my case the object whose event is being handled cannot be referenced because it’s out of scope - I couldn’t create a RemoveHandler statement that would compile.

I guess the simple solution is to make the URLConnection a module property rather than instantiating it in a method so it can be referenced at any time, but that’s a little OT from my original question. Maybe the answer to my original question is “Don’t do that”, but it seems nice and elegant apart from the pesky handlers that I was hoping would go away by themselves.

Technically you should be holding a reference to the socket yourself.

Delegates are instead of AddHandler.

The implication of what you say is that there’s another way of assigning a method as handler for an event, in this case a delegate method. Is this so?

This got me curious… i’m not getting @Greg_O_Lone 's point here.
Can you trow in an example ?

Maybe something near to these lines?

// DelegatableURLConnection.xojo_code

#tag Class
Protected Class DelegatableURLConnection
Inherits URLConnection
	#tag Event
		Sub Error(e As RuntimeException)
		  If ErrorEvent <> Nil Then
		    Try
		      ErrorEvent.Invoke(self, e)
		    Catch // Ignore errors at this level
		    End
		  End
		  
		End Sub
	#tag EndEvent

	#tag Event
		Sub FileReceived(URL As String, HTTPStatus As Integer, file As FolderItem)
		  If FileReceivedEvent <> Nil Then
		    Try
		      FileReceivedEvent.Invoke(self, URL, HTTPStatus, file)
		    Catch // Ignore errors at this level
		    End
		  End
		  
		End Sub
	#tag EndEvent


	#tag DelegateDeclaration, Flags = &h0
		Delegate Sub URLErrorDelegate(sender As DelegatableURLConnection, e As RuntimeException)
	#tag EndDelegateDeclaration

	#tag DelegateDeclaration, Flags = &h0
		Delegate Sub URLFileReceivedDelegate(sender As DelegatableURLConnection, url As String, httpStatus As Integer, file As FolderItem)
	#tag EndDelegateDeclaration


	#tag Property, Flags = &h0
		ErrorEvent As URLErrorDelegate
	#tag EndProperty

	#tag Property, Flags = &h0
		FileReceivedEvent As URLFileReceivedDelegate
	#tag EndProperty


	#tag ViewBehavior
		#tag ViewProperty
			Name="Name"
			Visible=true
			Group="ID"
			InitialValue=""
			Type="String"
			EditorType=""
		#tag EndViewProperty
		#tag ViewProperty
			Name="Index"
			Visible=true
			Group="ID"
			InitialValue="-2147483648"
			Type="Integer"
			EditorType=""
		#tag EndViewProperty
		#tag ViewProperty
			Name="Super"
			Visible=true
			Group="ID"
			InitialValue=""
			Type="String"
			EditorType=""
		#tag EndViewProperty
		#tag ViewProperty
			Name="Left"
			Visible=true
			Group="Position"
			InitialValue="0"
			Type="Integer"
			EditorType=""
		#tag EndViewProperty
		#tag ViewProperty
			Name="Top"
			Visible=true
			Group="Position"
			InitialValue="0"
			Type="Integer"
			EditorType=""
		#tag EndViewProperty
		#tag ViewProperty
			Name="AllowCertificateValidation"
			Visible=false
			Group="Behavior"
			InitialValue=""
			Type="Boolean"
			EditorType=""
		#tag EndViewProperty
		#tag ViewProperty
			Name="HTTPStatusCode"
			Visible=false
			Group="Behavior"
			InitialValue=""
			Type="Integer"
			EditorType=""
		#tag EndViewProperty
	#tag EndViewBehavior
End Class
#tag EndClass