Cocoa Distributed Objects "bycopy" Woes

Distributed Objects are one of the coolest features in Apple's Objective-C runtime, enabling communication between two threads or processes that do not even need to run on the same physical machine, using completely transparent proxy objects.

It's great, except for the few times where it behaves weirdly. I've stumbled across one of its inherent problems using the "bycopy" keyword recently.

"bycopy" means that you want the runtime not to send a proxy object for an argument or return value (that would be "byref", the default value), but to serialize (or marshal) the whole object and send a copy over to the receiver. This is useful for scenarios where a receiver might repeatedly access an object. When sending "byref", every access would mean that another message has to be sent across the connection, whereas "bycopy" sends more data over the connection once, but then the receiver has a full copy of the object, meaning it doesn't have to go over the proxy route to access it.

For most data types, this works great. For some of them, not so much. As an example, take an NSImage. It can have multiple representations, each being its own NSImageRep object. For some strange reason, even though NSBitmapImageRep (the one used in my particular scenario) supports NSCoding, I was getting inconsistent results when trying to draw an image sent over a DO connection, even though I was using "bycopy". Strangely, the image didn't even appear to be a proxy, but an actual copy, completely as expected, but still, drawing sometimes didn't work.

Similarly, I was getting weird results when sending an NSScreen object via "bycopy" and then trying to pass it to NSView's new enterFullscreenMode:withOptions: method (don't ask why I needed to send an NSScreen over a DO connection in the first place). Sometimes, the view failed to capture the screen properly.

To solve these issues, I opted for the following solutions: Rather than sending the NSImage "bycopy", I now send it "byref" and then use its TIFFRepresentation method to create a new, local NSImage from this data, effectively fetching the remote image data into local memory "manually". This might seem like a strange workaround and might even "destroy" some data (such as when the image has multiple representations at multiple resolutions), but it has solved all my problems drawing the image. It works fine now.

Similarly, rather than sending the NSScreen object, I now just send over the display number (actually of type CGDirectDisplayID), then re-create the NSScreen object locally from this display ID by enumerating the displays and getting the one with the matching ID. Again, problem solved.

The moral of this story is: If you're using Distributed Objects, but are getting weird, unreliable results when using objects sent over the connection, check your code to see if it works with locally created objects, even if you are using "bycopy". If it works with locally created objects, but not objects sent over DO using "bycopy", try to look for a way to "encode" your object in a more simple manner (such as using a unique ID) from which you can recreate the full object locally. This might save you a lot of headaches!