ios开发之向输出流写数据

来源:互联网 发布:windows 手写笔记软件 编辑:程序博客网 时间:2024/05/01 21:41

译自:Writing To Output Streams

使用NSOutputStream实例需要以下几个步骤:

1,使用存储写入数据的存储库创建和初始化一个NSOutputSteam实例,并且设置它的delegate。

2,将这个流对象布置在一个runloop上并且open the stream。

3,处理流对象向其delegate发送的事件消息。

4,如果流对象向内存中写入了数据,那么可以通过使用NSStreamDataWrittenToMemoryStreamKey属性获取数据。

5,当没有数据可供写入时,清理流对象。


一,使用流对象的准备工作

使用NSOutputStream对象之前你必须指定数据写入的流的目标位置,输出流对象的目标位置可以是file,C buffer, application memory,network socket。

NSOutputStream的初始化方法和工厂方法可以使用a file,a buffer, memory来创建和初始化实例,下面的代码初始化了一个NSOutputStream实例,用来向 application memory 写入数据。

- (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];}


上面的代码显示,在你初始化一个NSOutputStream对象之后应该设置它的delegate(通常是self),当流对象有 有空间可供数据写入 之类的与流有关的事件消息发送时,delegate会收到从NSOutputStream对象发送来的消息。

当你在open the stream对象之前,向流对象发送scheduleInRunLoop:forMode:消息使其在一个runloop上可以接收到stream events,这样,当流对象不能接收更多数据的时候,可以使delegate避免阻塞。当streaming发生在另外一个线程时,你必须将流对象布置在那个线程的run loop上,You should never attempt to access a scheduled stream from a thread different than the one owning the stream’s run loop. 最后 open the stream 开始数据向 NSOutputStream对象传送。


二,处理 Stream Events

当你向流对象发送open消息之后,你可以通过以下消息获取到流对象的状态,比如说当前是否有空间可供数据写入以及其他错误信息的属性。

  • streamStatus

  • hasSpaceAvailable

  • streamError

返回的状态是NSStreamStatus常量,它指示流当前的状态是opening,writing,at the end of the stream等等,返回的错误是NSError对象,它封装的是所有错误的信息。

重要的是,一旦open the stream,只要delegate持续想流对象写入数据,流对象就是一直向其delegate发送stream:handleEvent:消息,直到到达了流的末尾。这些消息中包含一个NSStreamEvent常量参数来指示事件的类型。对于一个NSOutputStream对象,最常见的事件类型是NSStreamEventOpenCompleted,NSStreamEventHasSpaceAvailable,NSStreamEventEndEncountered,delegate通常对NSStreamEventHasSpaceAvaliable事件最感兴趣。下面的代码就是处理NSStreamEventHasSpaceAvaliable事件的一种方法:

- (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:的实现中使用switch语句来判别NSStreamEvent常量,当这个常量是NSStreamEventHasSpacesAvailable的时候,delegate从NSMutableData对象_data中获取数据,并且将其指针转化为适合当前操作的类型u_int8.下一步计算即将进行写操作的字节数(是1024还是所有剩余的字节数),声明一段相应大小的buffer,向该buffer写入相应大小的数据,然后delegate调用流对象write:maxLength:方法将buffer中的数据置入output stream中,最后更新byteIndex用于下一次的读取操作。

如果delegate收到NSStreamEventHasSpacesAvailable事件消息但是没有向stream里写入任何数据,它不会从runloop再接收到space-available的事件消息直到NSOutputStream对象接收到数据,这样由于space-available事件该run loop会重新启动。如果这种情况很有可能在你的程序设计中出现,在收到NSStreamEventHasSpaceAvailable消息并且没有向该stream中写入数据时可以在delegate中设置一个标志位flag,之后,当存在更多的数据需要写入时,先检查该标志位,如果该标志位被设置,那么直接向output-stream实例写入数据。

对于一次向output-stream实例中写入多少数据没有严格的限定,一般情况下使用一些合理值,如512Bytes,1kB,4kB(一个页面大小)。

在向stream中写数据时NSOutputStream对象发生错误,它会停止streaming并且使用NSStreamEventErrorOccurred消息通知其delegate。


三,清理 Stream Object

当一个NSOutputStream对象结束向一个output stream写入数据,它通过stream:handleEvent:消息向delegate发送NSStreamEventEndEncountered事件消息,这个时候delegate应该清理 stream object,先关闭该stream object,从run loop中移除,释放该stream object。此外,如果NSOutputStream对象的目的存储库是application memory(也就是,你通过initToMemory方法或者其工厂方法outputStreamToMemory创建的该对象),现在就可以从内存中获取数据了。下面的代码实现的清理 stream object的工作:

- (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对象发送propertyForKey:消息获取从流向内存中写入的数据,设定key的值为NSStreamDataWrittenToMemoryStreamKey,该stream object将数据返回到一个NSData对象中。