Cocoa delegates

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.

Are you sure we don’t have such a class already in the MBS Plugins?

Like we have a couple of delegate implementing classes.

That explains a lot…

Christian I have not look at MBS yet

Do protocols have to be registered to be able to assign them?

objc_getProtocol(“MTKViewDelegate”) returns 0

Dev Source

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))

My problem is that objc_getProtocol(protocolName) returns 0 as it has not been loaded. Maybe I need to manually recreate the protocol.

r u on testing on iOS, if so I found this on apple’s forum, hope it helps…

Running on macos 10.12

I have read that if the protocol is not referenced in source it will not be loaded into runtime.

From another forum:

Found the answer in the Apple docs:

http://developer.apple.com/library/ios/#DOCUMENTATION/Cocoa/Conceptual/ObjectiveC/Chapters/ocProtocols.html#//apple_ref/doc/uid/TP30001163-CH15

The compiler creates a protocol object for each protocol declaration it encounters, but only if the protocol is also:

Adopted by a class,
Or referred to somewhere in source code (using @protocol())

@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 :slight_smile:

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