Чтение от входных потоков

В Какао, читающем из NSInputStream экземпляр состоит из нескольких шагов:

  1. Создайте и инициализируйте экземпляр NSInputStream из источника данных.

  2. Запланируйте потоковый объект на цикл выполнения и откройте поток.

  3. Обработайте события, о которых потоковый объект сообщает его делегату.

  4. Когда больше не будет данных для чтения, избавьтесь от потокового объекта.

Следующее обсуждение входит в каждый из этих шагов более подробно.

Подготовка потокового объекта

Начать использовать 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, можно узнать о его состоянии, имеет ли это байты в наличии для чтения, и природа ошибки со следующими сообщениями:

Возвращенное состояние 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 ...
    }
}