NSStream 和 Sockets,未调用 NSStreamDelegate 方法

Posted

技术标签:

【中文标题】NSStream 和 Sockets,未调用 NSStreamDelegate 方法【英文标题】:NSStream and Sockets, NSStreamDelegate methods not being called 【发布时间】:2011-02-08 08:31:19 【问题描述】:

我已按照指南 Setting Up Socket Streams 进行操作,并在课堂上有效地复制了该代码。无论我尝试什么,委托方法似乎都没有被调用。

在我的头文件中(基本上):

@interface myClass : NSObject <NSStreamDelegate> 
    NSInputStream *inputStream;
    NSOutputStream *outputStream;

- (void)connect;
@end;

连接方法:

- (void)connect 
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;

    CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)@"host.example.com", 1234, &readStream, &writeStream);

    inputStream = (NSInputStream *)readStream;
    outputStream = (NSOutputStream *)writeStream;
    [inputStream setDelegate:self];
    [outputStream setDelegate:self];
    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [inputStream open];
    [outputStream open];

还尝试使用CFStreamCreatePairWithSocketToCFHost()[NSStream getStreamsToHost:port:inputStream:outputStream: - 结果完全相同。

我在connect 方法的开头设置了一个断点,遍历了每一行,每个指针都是有效的,并且似乎指向了正确的对象。

在 GDB 中,setDelegate 调用后,po [inputStream delegate] 按预期打印&lt;myClass: 0x136380&gt;,因此它已正确设置委托。

对于我的生活,我无法弄清楚为什么它拒绝在我的班级上调用 stream:handleEvent: 方法:

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode 
    NSLog(@"got an event");

希望我错过了一些非常简单明了的东西,第二双眼睛可以发现我的错误。

提前感谢任何有耐心并花时间阅读本文的人!

【问题讨论】:

【参考方案1】:

在这样的行中:

[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

我没有使用[NSRunLoop currentRunLoop],而是将其更改为[NSRunLoop mainRunLoop]

编辑 2011-05-30:

这不起作用的原因是因为我正在通过+[NSThread detachNewThreadSelector:toTarget:withObject:] 在后台线程中设置套接字。

这样做会创建一个新的运行循环,在阅读run loop developer documentation 后我发现您需要告诉 NSRunLoop 手动运行。

在与主线程相同的运行循环中运行它的性能很好,尽管我可以通过编写包装类并在后台线程上运行所有网络 I/O 来挤出更多性能。

【讨论】:

@user102008 编辑了我的回复以解释。 你能贴出结束代码吗?出于某种原因,我将 [NSRunLoop currentRunLoop] 更改为 [NSRunLoop mainRunLoop] 并且连接在启动后不久就不会失败 你成功了!从主线程中删除我的套接字客户端后,我停止监听 inputStream(即使我的 outputStream 仍在工作,因为我可以在套接字服务器上进行监控)。我只是改变了输入流的运行循环和宾果游戏!赞成:) 您的问题和回答都帮助我知道去哪里,但我不确定如何使用简单的按钮开始和连接?? 非常感谢。我在使用 Monotouch 并在任务中打开套接字时遇到了同样的问题。使用主循环而不是当前(任务的线程)解决了这个问题。【参考方案2】:

该解决方案仅在线程 0 上没有阻塞工作时才有效。这通常没问题,但更好的解决方案是创建一个新线程(即使用类方法按需创建线程)然后在该线程上排队。 即

+ (NSThread *)networkThread 
    static NSThread *networkThread = nil;
    static dispatch_once_t oncePredicate;

    dispatch_once(&oncePredicate, ^
        networkThread =
             [[NSThread alloc] initWithTarget:self
                                     selector:@selector(networkThreadMain:)
                                       object:nil];
        [networkThread start];
    );

    return networkThread;


+ (void)networkThreadMain:(id)unused 
    do 
        @autoreleasepool 
            [[NSRunLoop currentRunLoop] run];
        
     while (YES);


- (void)scheduleInCurrentThread

    [inputstream scheduleInRunLoop:[NSRunLoop currentRunLoop]
                           forMode:NSRunLoopCommonModes];

有了这个,您可以使用以下方式安排输入流:

[self performSelector:@selector(scheduleInCurrentThread)
             onThread:[[self class] networkThread]
           withObject:nil
        waitUntilDone:YES];

这将允许您运行您的网络操作,而不必再担心死锁。

【讨论】:

感谢您的提示。我有另一个相关的问题。当流被写入时,用户可以继续在后台移动应用程序。我宁愿让流在后台运行。我尝试使用 beginBackgroundTask... 来实现这一点,但无论我做什么,当应用程序处于后台时,我都无法让我的图片上传工作。你这这一切都可能吗? 我们的一个教程应用程序可以做到这一点。如果您仍在寻求帮助,您可能想在parse.com/tutorials/anypic#post 上查看它,查看有关“保存 PFObject”的信息。虽然我们的框架隐藏了它,但后台任务中的位会进行网络调用。 这段代码是否定期充分耗尽自动释放池? -[NSRunLoop run] 的文档说它将重复调用 runMode:beforeDate:。这意味着在这些调用之间不会耗尽自动释放池。

以上是关于NSStream 和 Sockets,未调用 NSStreamDelegate 方法的主要内容,如果未能解决你的问题,请参考以下文章

背景上的 NSStream 没有调用

如何在 NSStream 中使用委托?

如何在 NSStream 中使用委托?

NSStream实现发送和接受数据

未调用记录器委托方法

iPhone:使用 NSStream 捕获连接错误