【问题标题】:NSStream and Sockets, NSStreamDelegate methods not being calledNSStream 和 Sockets,未调用 NSStreamDelegate 方法
【发布时间】: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");
}

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

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

【问题讨论】:

    标签: objective-c nsstream


    【解决方案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:。这意味着在这些调用之间不会耗尽自动释放池。
    猜你喜欢
    • 1970-01-01
    • 2012-09-17
    • 2015-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多