Not sure if this is new to 2013 or an existing bug, or just my misunderstanding…
I have a Class which creates a timer, adds a handler to that timer and when the timer fires, tries to Invoke a delegate for a method.
When I create the Delegate using WeakAddressOf, and then nil out the Class, I seem to be finding a situation where this code malfunctions:
if me.callback <> nil and me.callback isa CallbackMethod then
CallbackMethod(me.callback).invoke // < --- getting a NilObjectException here!
end if
Shouldn’t this be impossible? I thought the whole point of using WeakAddressOf was that if the Class it pointed to was destroyed, the delegate would also go to nil?
I ran into this problem today with a webapp. I was using WeakAddressOf to send a notification to a WebSession instance. But when a session is gone, there is no way to simply test if there is still something to invoke. You have to wrap the delegate call in a Try…Catch block. A simple test against nil, or a boolean method that return False if the delegate is no longer valid.
I have created a feature request: <https://xojo.com/issue/31185>
“Implement a method to inform about wether a WeakAddressOf delegate can be invoked safely or not”
Simply put: You have to get you into trouble if you want to avoid the trouble… Poor design indeed!
I think I put a couple of feedback requests for improvements to delegates. Like accessing the included reference to the object for the delegate. Maybe they can simply provide that as a variant property which may be the parent object or the weak ref to the parent object or nil if not applicable for the delegate.
Thanks Christian.
It seems that they all have been merged with the <https://xojo.com/issue/31185>. But I guess they are all private as I can’t see the merging notice in Feedback and Ssearching by their ID always leads to 31185.
Be aware with this issue that wrapping the Invoke in a Try/Catch will also catch exceptions caused inside the invoked method. If you don’t want that to happen, it means there is literally no way to code defensively.
Of course. Sometimes I feel unhappy by having no way to add features or access internals without someone telling me about some EULA rules.
Maybe you can simply go and change it for r1, so we have a property on the delegate with the target object? Or expose the weakref object of the delegate. Or whatever you have there.
Or maybe use a InvalidDelegateTargetException instead of a NilObjectException.
Yes, a new exception type would help too. In my case, I’m writing code other developers will use, and I really do not want to catch their exceptions for them. Even if the NOE had a unique error number or something I could use to determine this was the reason, that would be good enough.
Since the internals can change poking around in the internals can be unreliable over time and we end up getting the bug reports about the plugins not working.
Far better to as k for a sanctioned API in the SDK or, in this case, something that you can test in user code without having to muck about with internals.
WeakRefs and WeakAddressOf are objects. Should be great some way of temporally elevating it’s condition from Weak to “sustained” (go and add/subtract 1 to/from referenced object “ref counter”) like 2 methods like .hold() and .release() ?
// Current
Dim ref As WeakRef
If True Then
Dim f As New FolderItem
f.Name = "TestFile.txt"
ref = New WeakRef(f)
If ref.Value <> Nil Then
MsgBox(FolderItem(ref.Value).Name) // This displays
Else
MsgBox("f is Nil.")
End If
End If
If ref.Value <> Nil Then
MsgBox(FolderItem(ref.Value).Name)
Else
MsgBox("ref to f is gone.") // This displays
End If
// holding object example
Dim ref As WeakRef
If True Then
Dim f As New FolderItem
f.Name = "TestFile.txt"
ref = New WeakRef(f)
If ref.hold() Then // ref.hold() returns true if object life was held (ref counter +1), or false if it's already gone
MsgBox(FolderItem(ref.Value).Name) // This displays
Else
MsgBox("f is Nil, or referenced object is gone.")
End If
End If
If ref.Value <> Nil Then
MsgBox(FolderItem(ref.Value).Name) //Hey! it stills alive! We sustained a weak reference for a moment
Else
MsgBox("Weird. You should never be here. You couldn't hold the object for some reason.")
End If
ref.release() // you are now free to go my object, bye. (ref counter -1)