Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.

Call a D Method From Objective-C

Background

We need to somehow call the following D method from Objective-C:

class Foo
{
    void bar ();
}

To be able to call the above D method from Objective-C we need to know some things about how Objective-C works under the hood. Primary, what exactly an Objective-C method is. An Objective-C method is actually a regular C function that has a special signature, the simplest form is as follows:

void foo (id self, SEL sel);

The above is an Objective-C method that have no parameters and returns nothing (void). "self" is the instance that the method was called on and "sel" is the selector/method that was called. The function can of course have a return value and have several other parameters. The next step is to add the method to an existing Objective-C class or to a newly create one, see Create an Objective-C class?.

Calling a free function

The easiest is to call a free function from Objective-C. First you declare the function with C linkage as you would with any other function that would be called from C:

extern (C) void foo (id self, SEL sel);

Then you can call it from Objective-C or from D using the "objc_msgSend" function, see Call an Objective-C method form D.

Calling a D Method

Calling a method is a little trickier because Objective-C doesn't know anything about D classes or methods. But since it knows about C functions we can get it to work.

Creating a static method

The first step is to create a static method with C linkage, we call it "forwardVirtualCall". A static method in D is almost exactly like a free function.

class Foo
{
    void bar ();
    extern (C) static void forwardVirtualCall (id self, SEL sel, void* dObject);
}

The signature is almost like the previous example but we added a third parameter, which will be the D object we want to call the method on. We have to pass in the object we want to call because "this" is not available in a static method. The reason "void*" is used as the type is because the context pointer of a delegate is of that type. The implementation of "forwardVirtualCall" could look like this:

class Foo
{
    void bar ();

    extern (C) static void forwardVirtualCall (id self, SEL sel, void* dObject)
    {
        void delegate () dg;
        dg.ptr = dObject;
        dg.funcptr = &bar;
        dg();
    }
}

The code above creates a delegate and sets its two properties. First the context pointer to the object we want to call and then the function pointer to the method we want to call. Finally it calls the delegate. The code above would work, but only for final methods, not for virtual methods.

Resolve virtual method calls

To be able to call a virtual method we need another method, we call this "resolveVirtualCall", which looks like this (omitting other declarations):

void delegate () resolveVirtualCall ()
{
    return &bar;
}

"resolveVirtuallCal" returns a delegate to the method we want to call.

Putting it all together

Now we must change the "forwardVirtualCall" method to use the new "resolveVirtuallCal" method:

class Foo
{
    void bar ();

    void delegate () resolveVirtualCall ()
    {
        return &bar;
    }

    extern (C) static void forwardVirtualCall (id self, SEL sel, void* dObject)
    {
        void delegate () delegate () dg;
        dg.ptr = dObject;
        dg.funcptr = &resolveVirtualCall;
        dg()();
    }
}

What has changed in the code above is instead of a delegate returning void "forwardVirtualCall" declares a delegate which returns a delegate that returns void. Instead of "bar" as the function pointer we use the new "resolveVirtualCall" method. When we call the delegate we have to use two parentheses, because we first call the delegate and then the delegate that is returned. Now we can call the method, using methods available in the bridge, see Call an Objective-C method form D, as follows:

void* foo = cast(void*) new Foo;
Bridge.invokeObjecMethod!(void, "bar", void*)(self, foo);

D Abstractions and Extensions

It is quite a lot of code to implement just to make one D method callable from Objective-C but fortunately the bridge provides abstractions to make it a lot easier.

Binding methods

For binding methods the bride provides the template "ObjcBindMethod". It comes in three flavors and have the following signatures:

template ObjcBindMethod (alias method)
template ObjcBindMethod (alias method, string selector)
template ObjcBindMethod (alias method, R, string selector, ARGS...)

The description of the parameters are:

method
the method to bind
R
the return type of the method
selector
the selector to bind the method to
ARGS
the argument types of the method

The first version of "ObjcBindMethod" automatically infers the parameter types and the return type using the signature of the given method. It will also automatically build a selector using the signature of the method. When it builds the selector it will use the name of the given method and the names of the parameters as follows:

void foo (); // foo
void foo (Object sender); // foo:
void foo (int x, int y); // foo:y:
void foo (int x, int y, int z); // foo:y:z:

The second version of "ObjcBindMethod" automatically infers the parameter types and the return type but it does not build a selector. The last version does not infer anything. The code of the last version of "ObjcBindMethod" looks almost like the code above, in the section "Putting it all together", but with template functions instead. The template also does one more important thing that the above code doesn't do (if necessary), it encapsulate and decapsulate the parameter types and the return type.

"ObjcBindMethod" is used as follows:

class AppController : NSObject
{
	void foo (int i);
	mixin ObjcBindMethod!(foo); // now foo can be called from Objective-C using the selector "foo:"
}

"IBAction" is another template that is very similar to "ObjcBindMethod". It has the same signature as the fist version of the "ObjcBindMethod" templates. The only difference is that "IBAction" only accepts methods that have only one parameter and of the type Object (or any of its subclasses). The "IBAction" template has similar purpose as the "IBAction" macro in Objective-C. It indicates that a method is an action that is to be connected to a control (i.e. a button) using Interface Builder.

Binding class (static) methods and free functions

For binding class (static) methods the bridge provides the template "ObjcBindClassMethod". It comes in the same three versions as the "ObjcBindMethod" template. The only difference compared to "ObjcBindMethod" is that it does not have a "resolveVirtualCall" method.

"ObjcBindFunction" is used to bind regular free functions, it works just as "ObjcBindClassMethod" but there is only two versions:

template ObjcBindFunction (alias func)
template ObjcBindFunction (alias func, R, ARGS...)

The difference compared to "ObjcBindClassMethod" is that no selector is needed.

back