I am trying to pass an array of C-structs to an XPC Service, but the service receives only the first element. Following is the C struct
struct MyStruct
{
char *name;
unsigned char v1;
unsigned char v2;
Status status; // a c-style enum
};
and I am using it like this
struct MyStruct structs[3] = {{"name1", 0, 0, success}, {"name2", 1,1, success}, {0}};
[[_connectionToService remoteObjectProxy] doSomething:structs];
and doSomething
is declared as
- (void)doSomething: (struct MyStruct[]) structs;
The document Creating XPC Services mentions that C structures and arrays containing only the types listed above are supported, but I am unable to get it to work even for an array of c-strings or ints.
Also, there's an API for making synchronous RPC calls but there is no documentation available for it.
- (id)synchronousRemoteObjectProxyWithErrorHandler:(void (^)(NSError *error))handler
It does seem to block but only if the remote method has a reply block. Is this the expected behaviour? And is it safe to cache the proxy object returned by this method?
Filed
FB9991036
.
Thanks.
It does seem to be able to serialize the name field
Oh, you’re right. Wow, that’s smarter than I thought.
only the first struct in the array gets serialized.
Right. This boils down to how arrays are passed in C. When you declare a method like this:
- (void)doSomething:(struct MyStruct[])structs;
that’s equivalent to this:
- (void)doSomething:(struct MyStruct *)structs;
The parameter is passed as a pointer to the base of the array and there’s no info about its length.
C programmer usually get around this in one of two ways:
-
They give the array a terminator. For example, in the C string case, the array of
char
is terminated by anul
. -
They pass a second parameter with the count.
The first option is a non-starter for you. It’s possible that NSXPCConnection
is smart enough to interpret a count parameter next to the array as the array’s length. Honestly, I’ve never looked into it at that level of detail.
Based on the other details in your response I suspect that you’ll need to put a lot of work into your serialisation strategy, and thus you can deal with this at that time.
what does the documentation mean by C structures and arrays containing only the types listed above as acceptable parameters
I suspect it’s referring to fixed-size arrays within a C structure. For example:
struct Foo {
int a[5];
};
In this case there is compile-time knowledge about the length of the array.
Would any of these options help with function pointers?
Function pointers? Like this:
typedef void (*MyFunc)(void);
struct Foo {
MyFunc f;
};
No, nothing is going to help with those. Function pointers only make sense in the scope of a single process.
Some APIs in the library return some weird types involving pointers like a linked list where each node is itself a list of function pointers.
Yeah, that’s gonna be tricky. Serialising a linked list is easy enough but serialising a function pointer is impossible in the general case. The only situation where it might make sense is if the function pointer round trips. That is:
-
The service passes the function pointer to the app.
-
The app doesn’t do anything with the function pointer itself.
-
The app passes the function pointer back to the service.
In that situation you can serialise it by replacing the function pointer with a unique ID, maintaining a table that maps function pointers to unique IDs and another table for the reverse.
There’s a potential for serious security vulnerabilities here. Fortunately, the fact that you’re creating an app-specific XPC Service means that you don’t have to worry too much about this: Only your app can connect to this service.
Ideally for the lifetime of the application. The XPC service needs to run as long as the app is running and maintain state, the list of function pointers.
OK. Keeping a proxy around won’t help you achieve that goal. An existing proxy does not prevent the system from ‘garbage collecting’ your XPC Service.
What you need to do here is have the service maintain an open transaction while it holds in-memory state that’s relevant to the app. XPC automatically opens these transactions when the app sends a message to which the service hasn’t replied. However, in situations like this, where the service maintains state across requests and it’s not feasible to persist that state to disk, the service can arrange to stay in memory by opening a transaction and leaving it open. Do this using xpc_transaction_begin
.
IMPORTANT This doesn’t guarantee that the system won’t terminate the service. If things get dire, the system reserves the right to terminate services with open transactions. However, that’s not something that comes up in practice.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"