Запись в потоки вывода
Используя NSOutputStream
экземпляр для записи в поток вывода требует нескольких шагов:
Создайте и инициализируйте экземпляр
NSOutputStream
с репозиторием для записанных данных. Также установите делегата.Запланируйте потоковый объект на цикл выполнения и откройте поток.
Обработайте события, о которых потоковый объект сообщает его делегату.
Если потоковый объект записал данные в память, получите данные путем запроса
NSStreamDataWrittenToMemoryStreamKey
свойство.Когда больше не будет данных для записи, избавьтесь от потокового объекта.
Следующее обсуждение входит в каждый из этих шагов более подробно.
Подготовка потокового объекта
Начать использовать 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
, можно узнать о его состоянии, имеет ли это пространство для записи данных и природы ошибки со следующими сообщениями:
streamStatus
hasSpaceAvailable
streamError
Возвращенное состояние 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
объект.