This is nothing but a wild guess, but try changing/adding this:
[iStream open];
[oStream open];
CFRetain(self);
}
This is just for testing purposes, and you will likely 'leak' self
as a result. I base this recommendation because the stack trace crashes during some kind of callback processing, and you've set yourself as the delegate, so the hunch is that the callback target is you.
If this 'fixes' the problem, then I have no idea what the problem is (strange as it sounds).
BEGIN EDIT
Wouldn't you know it, right after I posted the answer, I think I found the problem. Make the following modification (don't add the CFRetain
bit):
[NSStream getStreamsToHost:host port:1234 inputStream:&iStream outputStream:&oStream];
iStream = iStream;
oStream = oStream;
IMPORTANT: both iStream
and oStream
must be ivars, part of your class declaration. Since the code snippet you posted leaves this open as a possibility, I just thought I'd be clear.
If its the bug I think it is, I'd be willing to bet this 'fixes' the problem too:
iStream = self;
oStream = self;
[NSStream getStreamsToHost:host port:1234 inputStream:&iStream outputStream:&oStream];
Presuming this is what I think it is, you should be able to remove the fixes, add sometihng like NSLog(@"self is: %p, iStream is: %p, oStream is: %p", self, iStream, oStream)
, and then wait for it to crash. Then, at the debugger, enter something like:
gdb> p *(MySelfClass *)0xdeadbeef
Where MySelfClass
is your class name, and 0xdeadbeef
is whatever the pointer is for self
. The values for iStream
and oStream
should be the same as whatever NSLog
spit out. Then, type in
gdb> info gc-references 0xPTR
gdb> info gc-roots 0xPTR
For each of self
, iStream
, oStream
. I think you'll find that the only thing the GC system knows about is self
, despite the fact that your ivars have pointers.
Another extremely frustrating problem I've had when using Cocoa's GC system is the compiler generates buggy code for __strong
pointers (I've filed several bugs regarding this). Most of the time this doesn't cause a crash because the problem is masked by another live pointer, made worse by the fact that 99% of the time both pointers tend to 'die' at nearly the same time. The GC system only performs a collection a few times per second at most, but usually much less frequently.
Then there are the times where there isn't another pointer around to mask the compilers mistake. This is a perfect example: you need those pointers to live past multiple run loop iterations. For these cases, you're eventually, at some point, going to cross paths with a collection run. When that happens is completely random, it could be several times per second, or once a minute.
END EDIT
There is a second 'solution' to your problem: don't use GC. In fact, I strongly recommend this, and this comes from practical experience of using this GC system since 10.5 beta in real world, non-trivial programs.
One of the reasons why I discourage its use is purely pragmatic: While debugging manual memory management / reference counted problems can be hard and a waste of time, it is virtually impossible to debug GC problems. I would bet any amount of money that your problem boils down to this: It is essentially equivalent a multi-threaded race condition bug. This is the most difficult class of problems to debug.
My guess is some where, some thing has dropped the __strong
qualifier to a GC managed pointer, and the biggest offender is a NSObject *object;
to a void *pointer;
, such as pointer = object;
. The compiler lets you do this without so much as a warning even though it leads to the kinds of problems you're having and is almost always an error, or opens up the potential for these kinds of errors. Sometimes its not even obvious that you've done this, such as passing an object to an argument that takes a void *
type. What's even worst is that in the later case there is nothing you can do: the method or function has not been compiled in such a way as to treat the received pointer as __strong
.
There is no generic way on how to handle these cases 'correctly' so your program behaves in a deterministic fashion and doesn't crash. There literally might not even be a way. When you inevitably encounter one of these corner cases, which are much more frequent than you'd think, or cause the problem yourself by forgetting __strong
, I promise you that any 'gains' in productivity that you were promised by using GC will be wiped out, to the point where not using GC would have been both much faster and much easier.
The next problem I encountered is performance problems. In the range of two to five times slower. This is because the GC system needs to be told when a GC manager pointer is stored somewhere in the heap. This is normally just a single instruction, but the compiler rewrites the assignment to a objc_assign_strongCast()
function call (!) that always acquires a global GC mutex lock (!!). This is a shockingly expensive operation.
This is a pretty heretical recommendation and I'm pretty sure that this will draw out Cocoa GC apologists in droves. The arguments basically boil down to:
Apples testing showed that for real world applications there was no performance impact.
This is not a defense, and it almost always comes from an Apple employee. In no way does this claim address, negate, or help solve the performance problems of my real world app
. And by academic standards, unless you're willing to provide the data and methodology that I can review and reproduce the same results, this claim can't even be taken seriously.
Lots of real world applications use GC.... like Xcode!
In all honesty, the only application I have ever seen mentioned is Xcode. One day (not that long ago) I actually went through the trouble to find all the GC using apps on my machine. There were two apps: Xcode.app
and /Developer/Applications/Utilities/Property List Editor.app/
. This includes whatever third party apps I had, along with the developer tools (obviously), standard OS X stuff, iLife and iWork '09. Setting aside the fact that this isn't an accurate sample of third party usage of GC, there is something very interesting that's not obvious at first: There are an awful lot of Apple applications that this covers, and there is exactly one real world app
in there: Xcode.app
.
This is made all the more interesting by the fact that Apple has apparently performed in house testing to determine GC impact on performance. I think it's safe to say that the apps covered would be some of the highest on the list to see if GC caused any impact to. This means that either Apple went to the effort of converting some of these apps to GC and then decided not to ship the GC version (which immediately raises the question of why not), or it raises some legitimate doubts as to just how many real world apps were used.