NSStream и Sockets, методы NSStreamDelegate не называются

Я следил за гидом Настройка 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] печатает <myClass: 0x136380>, как ожидалось, поэтому он правильно установил делегат.

В жизни я не могу понять, почему он отказывается вызывать метод stream:handleEvent: в моем классе:

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

Надеюсь, я пропустил что-то действительно простое и очевидное, а вторая пара глаз может обнаружить мою ошибку.

Спасибо заранее всем, у кого есть терпение, и потратил время, чтобы прочитать это далеко!

Ответ 1

В строках, подобных этому:

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

Вместо использования [NSRunLoop currentRunLoop] я изменил его на [NSRunLoop mainRunLoop].

EDIT 2011-05-30:

Причина этого не срабатывала, потому что я настраивал сокеты в фоновом потоке через +[NSThread detachNewThreadSelector:toTarget:withObject:].

Таким образом, созданный новый цикл запуска, который после прочтения документации по запуску цикла разработки показал, что вам нужно сообщить NSRunLoop запустить вручную.

Запуск в том же цикле запуска, что и основной поток, был хорош в производительности, хотя я смог выжать немного больше производительности, написав класс-оболочку и выполнив все сетевые операции ввода-вывода в фоновом потоке.

Ответ 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];

Это позволит вам выполнять сетевые операции, не беспокоясь о взаимоблокировках.