I'm using XPC to seperate a project into two projects - a main project, built for os x @ 64-bit, and an XPC service built for os x @ 32-bit, as it is using a library that is not available for 32 bit and cannot be replaced. The two communicate using NSXPCConnection.
I have a problem with the callbacks from the service to the main app:
when the reply takes a complicated argument (i.e: a class as nsimage or nsdata), is it not called back from the xpc service. There is no error or exception, simply nothing happens. this also occurs sometimes the other way, i.e., when calling the proxy object. It does not occur randomly, it happens only for certain reply signatures, though im not sure which.
I need to pass an nsimage in some way between the xpc service and the main app. I tried passing both NSImage and NSData, but the reply is not called (worth mentioning - it is being called when nil is passed instead of a real object).
I tried sending a pointer using [NSData bytes] then re-creating the data using [NSData initWithData:length:]. When I try this, the reply is called, but I get an EXC_BAD_ACCESS when calling initWithData. I assume this is because of some mess between the 32-bit and 64-bit addressing.
A few code samples:
When the reply takes an nsimage:
exported interface:
- (void)getImageWithReply:(void (^)(NSImage *image))reply;
reply called from:
- (void)getImageReply:(void (^)(NSImage *image))reply
{
//We always re-render. The responsibility to not re-render when not needed will be passed on.
[self render]; //updates puts in local var mRenderBuffer the render data
reply(nil, NO);
NSImage *image = ... //init image. it is being inited correctly, i know that
reply(image);
}'
When the reply takes a ptr to data:
exported interface
typedef long long XPC_PTR; //As void* has different sizes in 32/64bit env.
- (void)getRenderBufferWithReply:(void (^)(XPC_PTR ptr, uint size))reply;
reply called from:
- (void)getRenderBufferWithReply:(void (^)(READER_PTR ptr, uint size))reply
{
[self render];
reply((XPC_PTR)[mRenderBuffer bytes], mRenderWidth * mRenderHeight * BYTES_PER_PIXEL, YES);
}
It appears that all objects passed via the connection must conform to the NSSecureCoding protocol, which NSImage does not conform to.
NSData does conform to this protocol, and now I am passing it through the connection (not sure why it didn't work when I tried NSData earlier). I am creating the NSImage in the main process using the sent data.
The attempt to send a pointer was in hindsight a silly attempt, as the service and the main app naturally run of different processes and so they are mapped to different physical memory space.