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.