Wondering whats the best approach to implement delegates for Cocoa controls, classes. Any recommendations or examples will be greatly appreciated…
Im doing the basic calling Obj-C to create a class for a delegate (with/out protocols) and creating corresponding instances, all inspired by macoslib, ioskit and other projects found online.
Is it possible to uses instance methods as callbacks? (i only got it working with shared methods, when using instance methods the “ID” received was not the same (Ptr) of the delegate class instance I created in code)
I ended up with a “delegateClass” wrapper that I subclass, with a dictionary to keep track of classes instances already created and the actual delegate instances with its corresponding weakRef pointing to its owner.
I was looking on how to clean this dictionary, I notice that by the time I get the control “close” event or the destructor is invoked my instance Ptr is already null. I ended up running a ForEach every so often on the lifeCycle of delegateClass to look for Nil weakRef and removing them from the dictionary.
For each delegate a control implements a separate class instance of “delegateClass”, in general (most use-cases) in the callback (shared method) I end up calling a handler/method on the owner (weakref to control, canvas, etc). I tried implementing this using the actual control class (similar to how macoslib does it) with some helper functions to reduce the scaffolding but it was still quite a lot of work to repeat on each control/class (a bit ugly, the separate class made it cleaner). Is there anything else more elegant/reusable?
No. Instance methods are virtual, meaning there can be more than one implementation (i.e. method overriding) and the decision about which one to actually use is deferred until the method is actually invoked. Third party libraries don’t know how to make that decision, or even that a decision is needed. Also, virtual methods receive a hidden first parameter that refers back to the instance (the me/self reference), meaning virtual methods can never satisfy the callback’s signature.
I’m trying to set the delegate against MTKView. I registered a new subclass of NSObject with objc and added the methods with pointers to shared methods. The methods add successfully but once attached to the view the methods are never called.
I have had this working before with other delegates on iOS but I’m wondering if the protocol not existing is a problem.
For "MTKView " your protocol is “MTKViewDelegate”, I think it should be something in the lines of:
declare function NSClassFromString lib "Foundation" (name as CFStringRef) as Ptr
declare function objc_allocateClassPair lib "Foundation" (superclass as Ptr, name as CString, extraBytes as Integer) as Ptr
declare sub objc_registerClassPair lib "Foundation" (cls as Ptr)
declare function class_addMethod lib "Foundation" (cls as Ptr, name as Ptr, imp as Ptr, types as CString) as Boolean
declare function objc_getProtocol lib "Foundation" (name as CString) as Ptr
declare function class_addProtocol lib "Foundation" (Cls as Ptr, protocol as Ptr) as Boolean
dim className as text = "myMTKViewDelegate"
dim protocolName as text = "MTKViewDelegate"
dim myDelegateClass as Ptr = objc_allocateClassPair(NSClassFromString("NSObject"), className, 0)
class_addProtocol (myDelegateClass, objc_getProtocol(protocolName))
@Jose Cuevas RectControls being already implemented as Cocoa objects in Xojo, it is way easier to implement methods as extensions using the Handle, which has been done quite a bit in macoslib (see Additional Modules/Class Extensions).
Of course, Cocoa controls which are not implemented in Xojo, like NSSearchField or NSTokenField, need to be created from scratch from a RectControl (usually a Canvas).
About the implementation of delegates in Cocoa, I would advise you to at the NSSearchField implementation in macoslib. It creates a subclass of NSSearchField which includes the methods required by the delegate protocol. The reason is that actions are implemented as private methods so they are not accessible from another class like a “delegate (sub)class”.
If the protocol is missing you can manually build it yourself
Private Shared Function RegisterProtocols() as integer
//ensure MTKViewDelegate protocol exists
dim cName as CString = "MTKViewDelegate"
dim existing as integer = ObjectiveCRuntime.objc_getProtocol(cName)
if existing <> 0 then
return existing //all ok - found
end if
//does not exist so create it
dim proto as integer = ObjectiveCRuntime.objc_allocateProtocol(cName)
if proto = 0 then
//could not allocate name
return 0
end if
dim sel as integer
dim types as CString
//add the methods
sel = NSSelectorFromString("mtkView:drawableSizeWillChange:")
types = "v@:@{CGSize}"
ObjectiveCRuntime.protocol_addMethodDescription(proto,sel,types,true,true)
sel = NSSelectorFromString("drawInMTKView:")
types = "v@:@"
ObjectiveCRuntime.protocol_addMethodDescription(proto,sel,types,true,true)
//register the protocol
ObjectiveCRuntime.objc_registerProtocol(proto)
return ObjectiveCRuntime.objc_getProtocol(cName)
End Function
Then you can assign the protocol to your class as mentioned above