Чтение от входных потоков
В Какао, читающем из NSInputStream
экземпляр состоит из нескольких шагов:
Создайте и инициализируйте экземпляр
NSInputStream
из источника данных.Запланируйте потоковый объект на цикл выполнения и откройте поток.
Обработайте события, о которых потоковый объект сообщает его делегату.
Когда больше не будет данных для чтения, избавьтесь от потокового объекта.
Следующее обсуждение входит в каждый из этих шагов более подробно.
Подготовка потокового объекта
Начать использовать NSInputStream
объект у Вас должен быть (после первого определения местоположения, если необходимый) источник данных для потока. Источник данных может быть файлом, NSData
объект или сетевой сокет.
Инициализаторы и методы фабрики для NSInputStream позволяют Вам создавать и инициализировать экземпляр от NSData или файла. Перечисление 1 показывает экземпляр NSInputStream, создаваемый из файла.
Создание перечисления 1 и инициализация объекта NSInputStream
- (void)setUpStreamForFile:(NSString *)path { |
// iStream is NSInputStream instance variable |
iStream = [[NSInputStream alloc] initWithFileAtPath:path]; |
[iStream setDelegate:self]; |
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop] |
forMode:NSDefaultRunLoopMode]; |
[iStream open]; |
} |
Поскольку этот пример показывает после создания объекта, необходимо установить делегата (как правило, к self
). Делегат получает stream:handleEvent:
сообщения от NSInputStream возражают, когда тот объект планируется на цикл выполнения и имеет связанные с потоком события для создания отчетов, такой как тогда, когда существуют байты на потоке, который будет считан.
Перед открытием потока для начала потоковой передачи данных, отправьте a scheduleInRunLoop:forMode:
обменивайтесь сообщениями к потоковому объекту для планирования его для получения потоковых событий на цикле выполнения. Когда нет никаких данных по потоку для чтения, путем выполнения этого Вы помогаете делегату избежать блокировать. Если потоковая передача имеет место на другом потоке, несомненно, запланируют потоковый объект на цикл выполнения того потока. Вы никогда не должны пытаться получить доступ к запланированному потоку от потока, отличающегося, чем тот, владеющий циклом выполнения потока. Наконец, отправьте NSInputStream
инстанцируйте open
обменивайтесь сообщениями для запуска потоковой передачи данных из входного источника.
Обработка потоковых событий
После того, как потоковый объект отправляется open
, можно узнать о его состоянии, имеет ли это байты в наличии для чтения, и природа ошибки со следующими сообщениями:
streamStatus
hasBytesAvailable
streamError
Возвращенное состояние NSStreamStatus
постоянное указание, что поток открывается, чтение, в конце потока, и т.д. Возвращенная ошибка NSError
информация об инкапсуляции объекта о любой имевшей место ошибке. (См. справочную документацию для NSStream для описаний NSStreamStatus
и другие потоковые типы.)
Что еще более важно, как только потоковый объект был открыт, он продолжает отправлять stream:handleEvent:
сообщения его делегату, пока это не встречается с концом потока. Эти сообщения включают параметр с NSStreamEvent
постоянный, который указывает тип события. Для NSInputStream
объекты, наиболее распространенные типы событий NSStreamEventOpenCompleted
, NSStreamEventHasBytesAvailable
, и NSStreamEventEndEncountered
. Делегат обычно больше всего интересуется NSStreamEventHasBytesAvailable
события. Перечисление 2 иллюстрирует хороший подход для обработки этого типа события.
Перечисление 2 , Обрабатывающее доступное байтам событие
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode { |
switch(eventCode) { |
case NSStreamEventHasBytesAvailable: |
{ |
if(!_data) { |
_data = [[NSMutableData data] retain]; |
} |
uint8_t buf[1024]; |
unsigned int len = 0; |
len = [(NSInputStream *)stream read:buf maxLength:1024]; |
if(len) { |
[_data appendBytes:(const void *)buf length:len]; |
// bytesRead is an instance variable of type NSNumber. |
[bytesRead setIntValue:[bytesRead intValue]+len]; |
} else { |
NSLog(@"no buffer!"); |
} |
break; |
} |
// continued |
В этой реализации stream:handleEvent:
делегат использует оператор переключения для идентификации переданного - в NSStreamEvent
постоянный. Если константа NSStreamEventHasBytesAvailable
, делегат сначала лениво создает (если необходимый) NSMutableData
объект (_data
) содержать полученные байты. Тогда это объявляет буфер определенного размера (1 024 байта, в этом случае) и вызывает потоковый объект read:maxLength:
метод, заполняющий буфер конкретным количеством байтов. Если операция чтения успешно выбрала байты от потока, делегат добавляет эти байты к NSMutableData
объект.
Нет никакой твердой инструкции по сколько байтов для чтения когда-то. Несмотря на то, что может быть возможно считать все данные в потоке в одном событии, это зависит от длины потока (т.е. число байтов в нем), а также поведение ядра, включая характеристики устройства и сокета. Лучший подход должен использовать некоторый разумный размер буфера, такой как 512 байтов, один килобайт (как в примере выше), или размер страницы (четыре килобайта).
Когда NSInputStream
возразите сталкивается с ошибками, обрабатывающими поток, он прекращает передавать потоком и уведомляет своего делегата с a NSStreamEventErrorOccurred
. Делегат должен обработать ошибку в stream:handleEvent:
метод, как описано в Обработке Потоковых Ошибок.
Избавление от потокового объекта
Когда NSInputStream
объект достигает конца потока, это отправляет делегата a NSStreamEventEndEncountered
событие в a stream:handleEvent:
сообщение. Делегат должен избавиться от объекта путем выполнения зеркально-противоположного из того, что он сделал для подготовки объекта. Другими словами, это должно сначала закрыть потоковый объект, удалить его из цикла выполнения, и наконец выпустить его. Перечисление 3 дает пример того, как Вы могли бы сделать это.
Закрытие перечисления 3 и выпуск объекта NSInputStream
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode |
{ |
switch(eventCode) { |
case NSStreamEventEndEncountered: |
{ |
[stream close]; |
[stream removeFromRunLoop:[NSRunLoop currentRunLoop] |
forMode:NSDefaultRunLoopMode]; |
[stream release]; |
stream = nil; // stream is ivar, so reinit it |
break; |
} |
// continued ... |
} |
} |