Heres one thing where I couldnt get my head around:
Say I have a declared macOS object (like in the current case an AVAudioRecorder). It has an optional delegate class with two methods which Id like to forward as an event.
The basics are easy: Create an own subclass, either from NSObject or from AVAudioRecorder itself if I want to use it directly. The method calls are forwarded to two shared Xojo methods which pick the id delivered from the method, create an AVAudioRecorder (or delegate) object from this handle and, if its still alive, invoke a method that raises the event.
So far, everythings nice and working reliably. Only, while the event raiser does run, the event itself cannot be caught when I create a delegate object on the window or hold the Recorder instance alive in a window property or dictionary and try to use addHandler on it. It simply doesn’t fire.
My usual pattern of approaching such a problem is to install a shared dictionary where the macOS instance looks up itself the value in this case is a weakref of the connected control or controller class. Then I can invoke the event reliably on the control from the event raiser.
But in general, I dont like that dictionary handling that much and fear that performance could suffer from having a lot of control/declare object combinations alive.
I wonder if there are any better ways to handle such a problem? I thought of installing an iVar on the class itself but couldnt figure out how to send the weakref of the connected control to it.
I think there is no other way than having a dictionary of:
Keys: Objective-C Runtime pointers, and
Values: Xojo object instance wrapped in WeakRefs.
Only pointers need to be hashed, which I’m sure is fast. I’d recommend to not use a global dictionary but a shared one on each Xojo class/control/window.
It would be great if a Xojo class instance could be looked up by its internal pointer, then we could use objc_setAssociatedObject and objc_getAssociatedObject and store the reference to the Xojo instance with the Objective-C instance.
can’t you disconnect the logic of the delegate from the delegate itself?
I’m not familiar with the AVAudioRecorder delegates, but with the TextField and TextView delegates I have done something like this, where (WARNING : SWIFT EXAMPLE!!)
Thanks, both of you! @Dave: Eli explained the issue exactly. Its not about putting the method handler somewhere else, but forwarding the method call to the Xojo instance that contains the declared object. You wont find that in Swift. It would be great if one could store the Xojo controls weakRef in a property of the declared instance. But a Xojo weakRef does not convert to a ptr, and we dont have access to the Xojo object handle, so the workaround is to store it in a shared dictionary and retrieve the control from there for each “event” (delegate method call). Clearly not the optimum solution but apparently the best working one. Therefore:
Thanks a lot, @Eli! Your reassurance lead me at least to a solution where I can use AddHandler on the declared object that now fires reliably which is much better than the complicated event forwarder I used to use. Next would be some benchmarks to see if brute force (always forwarding inside a Try/Catch statement) or the clean way (checking for a Nil parent object) would be faster.
Do you know if a feedback request for revealing the object handle or the ptr to a weakRef exists?
I do not really understand what you mean by this. If you mean the dictionary lookup, just:
test the key for being Nil, if true, create a new Xojo object
then test to if the WeakRef is Nil, if true, create a new Xojo object
then test to if the WeakRef’s object is Nil, if true, create a new Xojo object
else use the object stored in the WeakRef.
I would not use Try…Catch for that. Testing for valid pointers and objects should always be in the form:
If p = Nil Then
If obj Is Nil Then
If WeakRef tests to be Nil, you can also immediately remove the pointer from the dictionary. But it is not really necessary when it comes to Xojo controls and windows, or redirected methods of classed create in the Objective-C runtime (like delegates or data sources) as there usually will not be many of them. So as long as you implement the stuff in each Xojo subclass separately, the dictionary with the pointerWeakRef-of-Xojo-objectmapping will stay small.
Which, frankly, is quite a brilliant approach and I remember spending days looking at that code trying to figure out what the hell they were doing there. Did you encounter any better method?
In general I agree. Some controls, like SpriteKitView or SceneKitView, should fire their events as fast as possible. In those cases, the parent control will exist almost all of the time except maybe for the small period of time where a window containing them would be closed and an event call could fire into Nilvana. So its something in the magnitude of a million to one or so, and in those cases Id prefer the fastest way, even if your advice is good coding practice of course.