Objective-C issues with threading and streams?
I don't really know for sure this is an issue with threads, but I don't know what else could be causing my problems. I have an iOS app that has an NSStreamDelegate invoking
- (void) stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
The delegate is given a handle to the AppDelegate, and in every other part of the StreamDelegate, the AppDelegate exists. However, in stream:handleEvent:, when I try to pass the results back to the AppDelegate for additional processing, I get an EXC_BAD_ACCESS error.
The only reason I think it might be a threading issue is that the documentation on stream:handleEvent: states:
The delegate receives this message only if theStream is scheduled on a run loop. The message is sent on the stream object’s thread. The delegate should examine streamEvent to determine the appropriate action it should take.
Does "the stream object's thread" refer to my StreamDelegate? Or is that a reference to stream being handed into the method? I'm assuming the later, but want to make sure I'm not mistaken.
If it is a different thread than what my AppDelegate sits on, then is that what is causing my problems? If so, then how do I handle it?
Edit: per @bbum, I'm updating to indicate what I've done.
After hacking around, I tried something I'd considered and then dismissed. I created, essentially, a dummy class that gets instantiated and called from stream:handleEvent:. I call the method that does the work with:
[man performSelectorOnMainThread:@selector(saveEvent:) withObject:jsonDict waitUntilDone:YES];
Inside of saveEvent:, I call back to the AppDelegate and (almost!) everything works from that point on.
I'm currently having an issue with the debugger complaining about trying to write to a file that doesn't exist. I don't know yet why it's doing so. The file name looks suspiciously like a hashed directory that gets created under "iPhone Simulator/5.0/Applications/" for the app, but I haven't figured out where, exactly, the error is occurring.
The really messed up part of this is that I simply am unable to get a stack trace on any crashes. I've tried using LLVM 3.0 and LLVM GCC 4.2, with the corresponding debuggers, but nothing. I'm using Xcode 4.2. I did not have this problem with 4.1.
Thanks for the words of wisdom, @bbum. Much appreciated. I'll post another update once I get this completely hashed out.
Edit(2011-10-18): [Update: Never mind; I'm an idiot. I was reading the stream into a byte array but not declaring an explicit size. I was running into some sort of buffer overrun. It's all working now.]
I've been pounding my head against the wall for a few days now, and then rewrote the code, due to the above errors. I'm again almost at the point of getting everything working, but the threading still seems to be biting me in the ass.
I'm getting the following error:
warning: Un开发者_JAVA百科able to restore previously selected frame.
What I'm doing is receiving the data on the NSInputStream, and writing it out, and then trying to refresh the view, to indicate the update. At some point after the calls are made, I should be seeing a call to viewWillAppear:(BOOL)animated, but the warning above comes up first and the app locks up.
I've googled extensively on the above, and usually the error comes about when there's some sort of infinite loop, or stack overflow, or something that is hammering memory. I can't see anything of that type happening. I still think I'm running into an issue where I've got my threads crossed, and the stack is getting confused. I just can't figure out why.
The saveEvent: selector being called above does its stuff, then modifies a property in the view to indicate it is dirty. This property gets touched. I see it happen when I put a break on it. But viewWillAppear: never gets called.
This has been ridiculously frustrating. It seems like such a simple thing, but what a mess this has been.
Does "the stream object's thread" refer to my StreamDelegate? Or is that a reference to stream being handed into the method? I'm assuming the later, but want to make sure I'm not mistaken.
There can only be one runloop per thread. The delegate will be called on whatever thread own's the runloop that the stream is scheduled on.
If it is a different thread than what my AppDelegate sits on, then is that what is causing my problems? If so, then how do I handle it?
Could likely be; if your AppDelegate isn't thread safe and the stream is scheduled on a non-main thread, than it is quite likely the problem.
Without more information, it is impossible to say more. Since the app is crashing, you should post the backtrace, at the least.
Did you resolve this? I am doing something very similar, and have it working although I am running into an issue with randomly not receiving all characters that should be appearing on the stream.
If it would help I can post the code that is working in my situation.
Effectively my code is start a background thread which the delegate then uses to monitor the stream.
-(void)backgroundThreadFunction:(NSObject*)obj
{
[self setBackgroundThread:[NSThread currentThread]];
[streamDelegate OpenSession:accessoryInfo];
while(_continueRunning) {
NSAutoReleasePool *_pool = [[NSAutoReleasePool alloc] init];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]];
[_pool release];
}
[self setBackgroundThread:nil];
}
And the Stream delegate OpenSession specifies to run on the current thread in the following manner.
[[currentSession inputStream] scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[[currenSession outputStream] scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
When I want the appDelegate to know something I normally use an NSNotification, which has been registered for the event.
[[NSNotificationCenter defaultCenter] postNotificationName:@"DataReceived" object:_receivedData];
Hope that helps.
Regards
精彩评论