For my current project, I had to implement interprocess communication between a background process and an application. In the past, I would probably use AppleEvents or even CFMessage, but this tech note advises against using those techniques.
After some investigation, I found that Distributed Objects is the easiest & cleanest way to do it.
Distributed Objects works its magic with a set of proxy classes that pose as the actual object which exists in the other application. Here’s how to use it.
The object you want to share can be any ordinary object, like this one:
@interface MyObject: NSObject - (int)myMethod; @end @implementation MyObject - (int)myMethod { return 42; } @end
On the server side, you create a connection and vend that object using NSConnection’s setRootObject: method.
#define SOCK_NAME "/var/tmp/com.example.socketname" // make sure socket file doesn't exist, or we'll fail unlink(SOCK_NAME); // create an AF_UNIX socket address struct sockaddr_un socketAddress; bzero(&socketAddress,sizeof(socketAddress)); socketAddress.sun_family = AF_UNIX; strcpy(socketAddress.sun_path,SOCK_NAME); socketAddress.sun_len = SUN_LEN(&socketAddress); NSData* socketAddressData = [NSData dataWithBytes:&socketAddress length: sizeof(socketAddress)]; NSPort * theMessagePort = [[NSSocketPort alloc] initWithProtocolFamily: AF_UNIX socketType: SOCK_STREAM protocol: 0 address: socketAddressData]; NSConnection * theConnection = [[NSConnection alloc] initWithReceivePort: theMessagePort sendPort: nil]; // create our object to be shared theObject = [[MyObject alloc] init]; [theConnection setRootObject: theObject];
In the client application, you create a remote connection and ask for a proxy object. You then use that proxy object as if it was the actual object.
// create an AF_UNIX socket address struct sockaddr_un socketAddress; bzero(&socketAddress,sizeof(socketAddress)); socketAddress.sun_family = AF_UNIX; strcpy(socketAddress.sun_path,SOCK_NAME); socketAddress.sun_len = SUN_LEN(&socketAddress); NSData* socketAddressData = [NSData dataWithBytes:&socketAddress length: sizeof(socketAddress)]; // Create a message handler socket and add it to our runloop NSPort * theMessagePort = [[NSSocketPort alloc] initRemoteWithProtocolFamily: AF_UNIX socketType: SOCK_STREAM protocol: 0 address: socketAddressData]; NSConnection * theConnection = [[NSConnection alloc] initWithReceivePort: nil sendPort: theMessagePort]; // this is actually a NSDistantObject which acts as a proxy to our actual object MyObject *anObject = (MyObject*)[theConnection rootProxy]; // call a method on the remote object int result = [anObject myMethod];
What really happens when you call the method in the client is that it gets packaged as a NSInvocation, which gets sent over the connection and is executed by the server process.