NSInputStream不会调用委托(stream:handleEvent :)

问题描述:

我在网上搜索了很长时间...我没有找到我的问题的答案,所以我决定在这里发布。 我尝试使用NSStream建立到NNTP服务器的连接。NSInputStream不会调用委托(stream:handleEvent :)

在测试程序中,我打开流并发送消息。代表方法(stream:handleEvent:)被调用两次输出流(NSStreamEventOpenCompleted,NSStreamEventHasSpaceAvailable),但从不输入流!

为什么输入流永远不会调用委托?有任何想法吗?

基本上,代码看起来是这样的:

init和打开流:

CFReadStreamRef tmpiStream; 
CFWriteStreamRef tmpoStream; 

CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)SERVER, PORT, &tmpiStream, &tmpoStream); 

iStream = (__bridge NSInputStream *) tmpiStream; 
oStream = (__bridge NSOutputStream *)tmpoStream; 

[iStream setDelegate:self]; 
[oStream setDelegate:self]; 

[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 
[oStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 

[iStream open]; 
[oStream open]; 

发送消息:

NSData *data = [[NSData alloc] initWithData:[messageString dataUsingEncoding:NSASCIIStringEncoding]]; 
[oStream write:[data bytes] maxLength:[data length]]; 

接收消息:

-(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode 
{ 
    NSLog(@"EventCode: %i", eventCode); 
    //switch-case-statement...(using constants - NSStreamEventOpenCompleted...) 
} 

类哪一个 包含代码从NSObjects继承并实现NSStreamDelegate。 (iOS5 ARC)

Thx任何帮助!

编辑: 我只是试图 “轮询” 开流这样的后 - 它的工作:

while (![iStream hasBytesAvailable]) 
{} 
uint8_t buffer[1024]; 
int len; 
NSString *str = @""; 

while ([iStream hasBytesAvailable]) 
{ 
    len = [iStream read:buffer maxLength:sizeof(buffer)]; 
    if (len > 0) 
    { 
     NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSISOLatin1StringEncoding]; 

     if (output != nil) 
     { 
      str = [str stringByAppendingString:output]; 
     } 
    } 
} 
NSLog(@"Response: %@", str); 

但是,可以肯定,我还需要更好的(异步)解决方案;)

是你让运行循环运行在默认模式下?

尝试检查输入流状态(-streamStatus)。

什么都记录到控制台?

您声称为输出流调用委托方法,但您的NSLog()调用不记录流。你确定?

最后,这与此无关,但如果您要将拥有的Core Foundation流对象的所有权转让给ARC,则应该使用__bridge_transfer转换。更好的是,使用CFBridgingRelease(),因为它与函数名称中的Create有明显的对称性。除非有您没有显示的CFRelease()调用,否则现有的代码正在泄漏这些流。

+0

是的,运行循环以默认模式运行。 没有日志。 NSLog() - 只是一个占位符......在我的项目中,我也为mem-leak-hint提供了流类型 thx。我会改变这个:) – matrau

我在这里找到你的答案,我有同样的问题。你的回答为我工作,但我觉得这工作得更好:

How to use delegate in NSStream?

要引用他的话:

运行循环未对委托方法运行足够长的 调用。

地址:

[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]]; 

打开流之后。这仅在没有GUI的程序 中必需 - 否则运行循环将为您旋转。

所以我的是这样的:

[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 
[self.inputStream open]; 
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:30.0]]; 

编辑: 然后,当你拥有的数据,比如我有我的所有数据完成后NSStreamEventEndEncountered委托方法流中出现:为handleEvent我把这个:

[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 
CFRunLoopStop(CFRunLoopGetCurrent()); 
[theStream close]; 

它在30.0s之前关闭运行循环,或者您设置它的时间长度。如果您的代码位于NSStreamHasBytesAvailable中,则与您的答案相同。

这使我仍然可以使用委托方法,并保持运行循环处于打开状态,直到下载完成,而不会一起跳过委托方法。

+0

所以这将从现在开始关闭'30.0'连接?我不认为这是解决方案。 – User

+0

附加说明如何不完整运行30秒。它应该和你的代码一样运行,如果正确的话,但是使用委托方法而不是阻塞。 – Diesel

我有同样的问题,然后我意识到,我不小心叫

[iStream setDelegate:self] 

我创建的IStream之前。