Sanity Check - Invoking Delegates

My app calls web services and I usually need the results of these calls so I set a delegate method and invoke it when I have the result.

Sometimes the object that called a web service goes out of scope before the service returns and, therefore, before the delegate method can be invoked. This means that the delegate method no longer exists. To attempt to deal with this, I have a try/catch around the callback method.

In the debugger (i.e. in the iOS Simulator) this try/catch successfully prevents a crash in these circumstances. However on the device my app seems to crash at this point.

Is there a better/right way to trap this situation?

use weak reference and check for nil

Thank you Antonio. I do use WeakAddressOf already and I do check for nil for my delegates.

However, looking into it a bit further, I have been using event definitions and calling AddHandler to map a real method to a definition and I don’t think it is possible to check for an event definition being nil.

Maybe I should change the definitions to be delegates so I can check for nil?

Edit: Maybe I should have called this thread “Is there a safe way to avoid calling event definitions when they might be nil and thus crash the app on the device?”

so you use:
addHandler myObject.someEvent, weakAddressOf myFunction

and when this event is called you get a Nil exception?

Yes and no.

So myObject.someEvent is an event definition I use addHandler and attach a method to it and most of the time I don’t get a nil object exception and all is well. But sometimes the object that contains the myFunction method goes out of scope by the time the event gets around to being called and so the event definition doesn’t have an associated method.

As I couldn’t work out how to test for this in the code I just wrapped it in a try/catch which seemed to work in the debugger but seems to cause crashes on the device.

Maybe I should change the way I’m doing this and use delegates instead of event definitions because at least with a delegate you can test for it being nil?

To correctly understand your issue: is the problem that myObject doesn’t exist anymore meaning you are looking for a way to keep it alive, or is it that you want to check in the callback if myObject still exists or not?

the use of weakAddressOf should avoid this.
If the the event is fired it has no call to do since the function has been niled.

but I’ve just done a test and is not working so.

a workaround could be:
add a closing event to you niling object and in the Destructor method raise it (or if it’s a control use its close event)
in the closing (or close) event remove the handler.

Now it should work as expected.

Thank you Antonio. I’ll give it a try.

You could also try using just AddressOf with AddHandler. That counts as a Reference and so the target object will not go out of scope until you’re done with it.

Thanks Greg. And I guess this is a “scope 101” type question, but what happens if I use AddressOf with AddHandler but then destroy my target object before the callback? Will it be destroyed or will it hang around?

It would hang around. If this is a one off event, just be sure to call RemoveHandler at the end of the method.

I don’t know how this relates to your specific question, Jason, but just as a general advice: Many of the iOS (and OS X) classes that carry a delegate class hold the delegate as an unretained (in Xojo terms: Weakref) object. Instead of creating a separate delegate class together with a rather complicated shared class dictionary of weakrefs holding the original object as the value for the delegate id, I attach the delegate methods to my own subclass in those cases instead. If the object itself goes out of scope, its delegate ceases to exists then too.
But, of course, if the delegate is a retained property you need to work with a weakref dictionary.

OK so thank you all for sticking with this thread. :slight_smile:

I went ahead and removed the definitions, replacing them with delegates. I was thinking that I could test for the delegate being nil. But of course, once it is assigned, it isn’t nil even if the target of the delegate has been destroyed.

So now I’m back where I started, catching it with a try/catch. And I am starting to wonder whether there is a bug in the framework that enables this to be handled in the debugger but seems to trigger a hard crash on the device?

Can you show some code?

Seconding Eli.
How did you implement your delegates, Jason? Usually you would create them with a public dictionary which holds their own id as key and the weakreaf to their parent object. So the delegate is always called regardless of the parent’s existence but then checks for its parent being alive before trying to run code on it.
You seem to be following a different approach when your delegate goes out of scope together with the parent. Sometimes it is necessary to create shared Retain-Arrays or -Dicts to keep the delegate alive. Using these methods, it is surely possible to create delegates without them running code on a try/catch base.

Thanks Ulrich but can you point me to a patterns & practices example on this? It seems to be unusually cumbersome to have to create an additional index on the delegates simply to verify that they are still valid. I didn’t see that in the Xojo example project that I used when initially learning about delegates.

In my case, a member of an array of objects which is a property of a view instantiates a new instance of a class that calls one of my web services and passes a WeakRef into the constructor of the class representing a call-back method as a delegate. Most of the time, the calling object still exists when the instantiated web service class needs to call back to it, but sometimes it has been destroyed. I’m not sure where I’d place the dictionary to act as a separate reference counter on this except in some global module but that doesn’t seem very, er, “neat and tidy” either.

Jason, it is really difficult to understand what is going on exactly from prose – source code would help very much.

If the object initiating the round-trip to the web service must be alive when the callback calls back, you need to reference it somewhere - plain and simple. In a module, in App, or in any object that is guaranteed to be alive and therefore will keep alive this initiating object.

Thanks Eli but I don’t think source will illuminate this any more as I’m not doing anything magical. I’m creating an instance of my class, which is based on HTTPSocket, and setting a delegate for it to call back to. That’s it. The class executes asynchronously and sometimes the calling object no longer exists when it calls back. The object does not need to be alive, as you say, and in fact it won’t always be alive. That is the problem. I need to be able to handle a situation when it isn’t alive. I can do that in the debugger with try/catch but that does not seem to work on an iOS device.

[quote=195687:@Antonio Rinaldi]
but I’ve just done a test and is not working so.

a workaround could be:
add a closing event to you niling object and in the Destructor method raise it (or if it’s a control use its close event)
in the closing (or close) event remove the handler.

Now it should work as expected.[/quote]

I think a version of this method might work in my situation. The delegate properties in my class are currently private and set within the class constructor, but if I make them public I could nil them in the destructor method of my calling object. Then my check for the delegate being nil before calling the delegate’s Invoke method would work.

No, that’s not it. It is not clear what this means. For example: is the delegate instance a property of the HTTPSocket? And there are more questions to fully understand what’s going on – but I am out now as trying to help seems not being appreciated.