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];
Это позволит вам выполнять сетевые операции, не беспокоясь о взаимоблокировках.