Запись в потоки вывода

Используя NSOutputStream экземпляр для записи в поток вывода требует нескольких шагов:

  1. Создайте и инициализируйте экземпляр NSOutputStream с репозиторием для записанных данных. Также установите делегата.

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

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

  4. Если потоковый объект записал данные в память, получите данные путем запроса NSStreamDataWrittenToMemoryStreamKey свойство.

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

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

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

Начать использовать NSOutputStream объект необходимо указать место назначения для данных, записанных в поток. Место назначения для объекта потока вывода может быть файлом, буфером C, памятью приложения или сетевым сокетом.

Инициализаторы и методы фабрики для NSOutputStream позвольте Вам создавать и инициализировать экземпляр с файлом, буфером или памятью. Перечисление 1 показывает создание NSOutputStream экземпляр, который запишет данные в память приложения.

  Создание перечисления 1 и инициализация NSOutputStream возражают для памяти

- (void)createOutputStream {
    NSLog(@"Creating and opening NSOutputStream...");
    // oStream is an instance variable
    oStream = [[NSOutputStream alloc] initToMemory];
    [oStream setDelegate:self];
    [oStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
        forMode:NSDefaultRunLoopMode];
    [oStream open];
}

Поскольку код в Перечислении 1 показывает после создания объекта, необходимо установить делегата (как правило, к self). Делегат получает stream:handleEvent: сообщения от NSOutputStream возразите, когда тот объект имеет связанные с потоком события для создания отчетов, такой как тогда, когда поток имеет пространство для байтов.

Перед открытием потока для начала потоковой передачи данных, отправьте a scheduleInRunLoop:forMode: обменивайтесь сообщениями к потоковому объекту для планирования его для получения потоковых событий на цикле выполнения. Когда поток неспособен принять больше байтов, путем выполнения этого Вы помогаете делегату избежать блокировать. Если потоковая передача имеет место на другом потоке, несомненно, запланируют потоковый объект на цикл выполнения того потока. Вы никогда не должны пытаться получить доступ к запланированному потоку от потока, отличающегося, чем тот, владеющий циклом выполнения потока. Наконец, отправьте NSOutputStream инстанцируйте open обменивайтесь сообщениями для запуска потоковой передачи данных к выходному контейнеру.

Обработка потоковых событий

После того, как потоковый объект отправляется open, можно узнать о его состоянии, имеет ли это пространство для записи данных и природы ошибки со следующими сообщениями:

Возвращенное состояние NSStreamStatus постоянное указание, что поток открывается, запись, в конце потока, и т.д. Возвращенная ошибка NSError информация об инкапсуляции объекта о любой имевшей место ошибке. (См. справочную документацию для NSStream для описаний NSStreamStatus и другие потоковые типы.)

Что еще более важно, как только потоковый объект был открыт, он продолжает отправлять stream:handleEvent: сообщения его делегату (как долго, поскольку делегат продолжает помещать байты на поток), пока он не встречается с концом потока. Эти сообщения включают параметр с NSStreamEvent постоянный, который указывает тип события. Для NSOutputStream объекты, наиболее распространенные типы событий NSStreamEventOpenCompleted, NSStreamEventHasSpaceAvailable, и NSStreamEventEndEncountered. Делегат обычно больше всего интересуется NSStreamEventHasSpaceAvailable события. Перечисление 2 иллюстрирует один подход, который Вы могли проявить для обработки этого типа события.

Перечисление 2  , Обрабатывающее доступное пространству событие

- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
{
    switch(eventCode) {
        case NSStreamEventHasSpaceAvailable:
        {
            uint8_t *readBytes = (uint8_t *)[_data mutableBytes];
            readBytes += byteIndex; // instance variable to move pointer
            int data_len = [_data length];
            unsigned int len = ((data_len - byteIndex >= 1024) ?
                1024 : (data_len-byteIndex));
            uint8_t buf[len];
            (void)memcpy(buf, readBytes, len);
            len = [stream write:(const uint8_t *)buf maxLength:len];
            byteIndex += len;
            break;
        }
        // continued ...
    }
}

В этой реализации stream:handleEvent: делегат использует оператор переключения для идентификации переданного - в NSStreamEvent постоянный. Если константа NSStreamEventHasSpacesAvailable, делегат содержал байты a NSMutableData объект (_data) и совершенствует указатель для текущей операции записи. Это затем определяет мощность байта нависшей операции записи (1024 или остающиеся байты для записи), объявляет буфер того размера и копирует тот объем данных в буфер. Затем делегат вызывает объект потока вывода write:maxLength: метод для помещения содержания буфера на поток вывода. Наконец это совершенствуется, индекс раньше совершенствовался readBytes указатель для следующей работы.

Если делегат получает NSStreamEventHasSpaceAvailable событие и ничего не пишет в поток, оно не получает дальнейшие доступные пространству события от цикла выполнения до NSOutputStream объект получает больше байтов. Когда это происходит, цикл выполнения перезапущен для доступных пространству событий. Если этот сценарий вероятен в Вашей реализации, Вы можете сделать, чтобы делегат установил флаг, когда это не пишет в поток после получения NSStreamEventHasSpaceAvailable событие. Позже, когда Ваша программа имеет больше байтов для записи, она может проверить этот флаг и, если установлено, записать в экземпляр потока вывода непосредственно.

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

Когда NSOutputStream возразите сталкивается с ошибками, пишущими в поток, он прекращает передавать потоком и уведомляет своего делегата с a NSStreamEventErrorOccurred. Делегат должен обработать ошибку в stream:handleEvent: метод, как описано в Обработке Потоковых Ошибок.

Избавление от потокового объекта

Когда NSOutputStream объект завершает данные записи к потоку вывода, это отправляет делегата a NSStreamEventEndEncountered событие в a stream:handleEvent: сообщение. В этой точке делегат должен избавиться от потокового объекта путем выполнения зеркально-противоположного из того, что это сделало для подготовки объекта. Другими словами, это должно сначала закрыть потоковый объект, удалить его из цикла выполнения, и наконец выпустить его. Кроме того, если место назначения для NSOutputStream объект является памятью приложения (т.е. Вы создали использование экземпляра initToMemory или метод фабрики outputStreamToMemory), Вы могли бы теперь хотеть получить данные, сохраненные в памяти. Перечисление 3 иллюстрирует, как Вы могли бы сделать все эти вещи.

  Закрытие перечисления 3 и выпуск объекта NSInputStream

- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
{
    switch(eventCode) {
        case NSStreamEventEndEncountered:
        {
            NSData *newData = [oStream propertyForKey:
                NSStreamDataWrittenToMemoryStreamKey];
            if (!newData) {
                NSLog(@"No data written to memory!");
            } else {
                [self processData:newData];
            }
            [stream close];
            [stream removeFromRunLoop:[NSRunLoop currentRunLoop]
                forMode:NSDefaultRunLoopMode];
            [stream release];
            oStream = nil; // oStream is instance variable
            break;
        }
        // continued ...
    }
}

Вы записали потоковые данные в память путем отправки NSOutputStream объект a propertyForKey: сообщение, указывая ключ NSStreamDataWrittenToMemoryStreamKey Потоковый объект возвращает данные в NSData объект.