NSStream和Sockets,未调用NSStreamDelegate方法

arr*_*hiu 12 objective-c nsstream

我遵循了设置套接字流的指南,并在我的课程中有效地复制了该代码.无论我尝试什么代理方法,似乎都没有被调用.

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

@interface myClass : NSObject <NSStreamDelegate> {
    NSInputStream *inputStream;
    NSOutputStream *outputStream;
}
- (void)connect;
@end;
Run Code Online (Sandbox Code Playgroud)

连接方法:

- (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];
}
Run Code Online (Sandbox Code Playgroud)

也尝试使用CFStreamCreatePairWithSocketToCFHost()[NSStream getStreamsToHost:port:inputStream:outputStream:- 所有具有完全相同的结果.

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

在GDB中,在setDelegate调用之后,按预期po [inputStream delegate]打印<myClass: 0x136380>,因此它已正确设置委托.

对于我的生活,我无法理解为什么它拒绝stream:handleEvent:在我班上调用这个方法:

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
    NSLog(@"got an event");
}
Run Code Online (Sandbox Code Playgroud)

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

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

arr*_*hiu 27

在这样的行:

[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
Run Code Online (Sandbox Code Playgroud)

而不是使用[NSRunLoop currentRunLoop]我改变它[NSRunLoop mainRunLoop].

编辑2011-05-30:

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

这样做就创建了一个新的运行循环,在阅读了运行循环开发人员文档后,我发现你需要告诉NSRunLoop手动运行.

在与主线程相同的运行循环中运行它在性能上是很好的,尽管我通过编写包装类并在后台线程上运行所有网络I/O来提高性能.


Tho*_*din 9

当且仅当您在线程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];
}
Run Code Online (Sandbox Code Playgroud)

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

[self performSelector:@selector(scheduleInCurrentThread)
             onThread:[[self class] networkThread]
           withObject:nil
        waitUntilDone:YES];
Run Code Online (Sandbox Code Playgroud)

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

  • 此代码是否会定期充分消耗自动释放池?` - [NSRunLoop run]`的文档说它将重复调用`runMode:beforeDate:`.这意味着自动释放池不可能在这些调用之间耗尽. (2认同)