Frustration
Have just spent about six hours trying to track down a bug in my code that was causing a crash in Synergy Advance. Turns out that it wasn’t a bug in my code at all, rather it was a misunderstanding of the way NSConnection
works. I was using an NSConnection
to communicate between threads, passing a pointer to an NSInvocation
from one thread to another. The receiving thread was testing for pointer equality prompting my assertion to fail. The reason I spent so much time before finding the cause is that I assumed it was an error in my code; it wasn’t until the very end that I decided to test my assumptions about the way Cocoa works…
It turns out that because NSConnection
uses NSDistantObject
, a subclass of NSProxy
, to do the communication it wasn’t just passing my pointer; it was actually creating a new NSInvocation
on the fly, and passing a pointer to that object! I guess I should have known this if I’d’ve had more experience with distributed objects. A painful and frustrating lesson, nonetheless. I guess I’m a little surprised that it works this way; I can easily handle thread-safe access to the object, all I want to do is perform a pointer comparison!
So the "solution" is to pass something else. My first attempt was to pass an NSValue
object containing the pointer, but that causes causes an NSInvalidArgumentException
(cannot encode (void *) value: <170ff900 >). Seems like the way to go will be to use NSNumber
to represent the pointer… Unbelievable.
Of course, once you’ve learned (the hard way, using the debugger) that this is the way NSConnection
works, you quickly find some explanation in the documentation. In the description for the rootProxy
method of NSConnection
you click on the link to NSDistantObject
, and from there to the Distributed Objects manual, where you find a heading, "Making Substitutions During Message Encoding", which tells all.
At the end of your journey you find good old NSPortCoder
and see that it has two methods, isByCopy
and isByRef
which report whether the coder will encode by copying (the behaviour you’re trying to avoid) or by reference (but is this the desired behavior?; I suspect that what actually gets passed is an NSDistantObject
proxy and not the real NSInvocation
). But you’ve now got your working (but ugly) NSNumber
workaround, so the question becomes, how much work will it take to force your NSConnection
to pass the invocation as an unmodified pointer instead of by copying, and will it be worth it? Will you have to subclass NSConnection
? Or NSInvocation
? Or NSPortCoder
? Might you even have to subclass NSDistantObject
?
I’m going to explore these questions and see what I find out.