zoukankan      html  css  js  c++  java
  • [ios]流操作 【转】

    http://blog.csdn.net/caryaliu/article/details/7658536

    向输出流写数据

    使用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 写入数据。

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


    上面的代码显示,在你初始化一个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事件的一种方法:

    1. - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode  
    2. {  
    3.     switch(eventCode) {  
    4.         case NSStreamEventHasSpaceAvailable:  
    5.         {  
    6.             uint8_t *readBytes = (uint8_t *)[_data mutableBytes];  
    7.             readBytes += byteIndex; // instance variable to move pointer  
    8.             int data_len = [_data length];  
    9.             unsigned int len = ((data_len - byteIndex >= 1024) ?  
    10.                 1024 : (data_len-byteIndex));  
    11.             uint8_t buf[len];  
    12.             (void)memcpy(buf, readBytes, len);  
    13.             len = [stream write:(const uint8_t *)buf maxLength:len];  
    14.             byteIndex += len;  
    15.             break;  
    16.         }  
    17.         // continued ...  
    18.     }  
    19. }  

    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的工作:

    1. - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode  
    2. {  
    3.     switch(eventCode) {  
    4.         case NSStreamEventEndEncountered:  
    5.         {  
    6.             NSData *newData = [oStream propertyForKey:  
    7.                 NSStreamDataWrittenToMemoryStreamKey];  
    8.             if (!newData) {  
    9.                 NSLog(@"No data written to memory!");  
    10.             } else {  
    11.                 [self processData:newData];  
    12.             }  
    13.             [stream close];  
    14.             [stream removeFromRunLoop:[NSRunLoop currentRunLoop]  
    15.                 forMode:NSDefaultRunLoopMode];  
    16.             [stream release];  
    17.             oStream = nil; // oStream is instance variable  
    18.             break;  
    19.         }  
    20.         // continued ...  
    21.     }  
    22. }  

    通过向NSOutputStream对象发送propertyForKey:消息获取从流向内存中写入的数据,设定key的值为NSStreamDataWrittenToMemoryStreamKey,该stream object将数据返回到一个NSData对象中。

    输入流里读入数据

    ios cocoa 编程,从NSInputStream中读入数据包括几个步骤:

    1.从数据源创建和初始化一个NSInputStream实例

    2.将输入流对象配置到一个run loop,open the stream

    3. 通过流对象的delegate函数处理事件

    4. 当所有数据读完,进行流对象的内存处理


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

    在使用NSInputStream对象之前你必须有流的数据源,数据源的类型可以是文件,NSData对象,或者一个网络套接字。

    NSInputStream的初始化函数和工厂方法可以从NSData和文件创建和初始化一个NSInputStream的实例。下面的例子是从文件创建一个NSInputStream的实例:

    1. - (void)setUpStreamForFile:(NSString *)path {  
    2.     // iStream is NSInputStream instance variable  
    3.     iStream = [[NSInputStream alloc] initWithFileAtPath:path];  
    4.     [iStream setDelegate:self];  
    5.     [iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]  
    6.         forMode:NSDefaultRunLoopMode];  
    7.     [iStream open];  
    8. }  

    上面的例子显示,当你创建对象之后你应该设置其delegate。当把NSInputStream对象配置到一个run loop,并且有与流相关的事件(例如流中有可读数据)发生时,该对象会收到stream:handleEvent:消息。

    在你open stream之前,给流对象发送一个scheduleInRunLoop:forMode: 消息,来将该对象配置到一个run loop接收stream events。这样,当流中没有数据可读时可以避免delegate阻塞。如果流是发生在另一个线程,你需要确认该流对象是配置在那个线程的run loop中。你不应该尝试从一个除了包含该流对象的run loop的线程的其他线程中对流进行操作。最后,对NSInputStream对象发送open消息开始对输入数据的流操作。


    二,处理Stream Events

    当你对一个流对象发送open消息之后,你可以查找到它的当前状态。通过下面的消息可以知道流对象中是否有数据可读,以及任何错误的属性:

    • streamStatus

    • hasBytesAvailable

    • streamError

    返回的状态是一个NSStreamStatus常量,它可以指示流对象是处于opening,reading,或者at the end of the stream等等。返回的错误是一个NSError对象,它封装了可能发生的所有错误信息。

    重要的是,一旦 open 流对象,流对象会一直向其delegate发送stream:handleEvent: 消息直到到达了流对象的末尾。这些消息的参数中包含一个指示流事件类型的NSStreamEvent常量。对NSInputStream对象而言,最常用的事件类型是NSStreamEventOpenCompleted,NSStreamEventHasBytesAvailable,NSStreamEventEndEncountered。我们尤其感兴趣的应该是NSStreamEventHasBytesAvailable事件。下面的例子就是一个处理NSStreamEventHasBytesAvailable事件的好的方法:

    1. - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {  
    2.    
    3.     switch(eventCode) {  
    4.         case NSStreamEventHasBytesAvailable:  
    5.         {  
    6.             if(!_data) {  
    7.                 _data = [[NSMutableData data] retain];  
    8.             }  
    9.             uint8_t buf[1024];  
    10.             unsigned int len = 0;  
    11.             len = [(NSInputStream *)stream read:buf maxLength:1024];  
    12.             if(len) {  
    13.                 [_data appendBytes:(const void *)buf length:len];  
    14.                 // bytesRead is an instance variable of type NSNumber.  
    15.                 [bytesRead setIntValue:[bytesRead intValue]+len];  
    16.             } else {  
    17.                 NSLog(@"no buffer!");  
    18.             }  
    19.             break;  
    20.         }  
    21.         // continued  
    22. }  

    stream:handleEvent: 函数使用switch语句来判别NSStreamEvent常量,当这个常量是MSStreamEventHasBytesAvailable的时候,delegate函数会lazy create 一个NSMutableData对象_data来接收读取的数据。然后声明一个大小为1024的uint8_t类型数组buf,调用read:maxLength:函数从stream中读取指定大小的数据到buf中,如果读取成功,delegate将会将读取到的数据添加到NSMutableData对象_data中,并且更新总的读取到的数据bytesRead.

    至于一次从stream中读取多大的数据,一般来说,使用一些常用的数据大小规格,比如说512Bytes,1kB,4kB(一个页面大小)。


    三,处理stream object

    当NSInputStream对象到达steam的末尾的时候,它会向stream:handleEvent:函数发送一个NSStreamEventEndEncountered事件类型常量,delegate函数应该做出与准备使用流对象相反的操作,也就是说,需要关闭流对象,从run loop中移除,最终释放流对象。如下面的代码所示:

      1. - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode  
      2. {  
      3.     switch(eventCode) {  
      4.         case NSStreamEventEndEncountered:  
      5.         {  
      6.             [stream close];  
      7.             [stream removeFromRunLoop:[NSRunLoop currentRunLoop]  
      8.                 forMode:NSDefaultRunLoopMode];  
      9.             [stream release];  
      10.             stream = nil; // stream is ivar, so reinit it  
      11.             break;  
      12.         }  
      13.         // continued ...  
      14.     }  

      15. 通常情况下,特别是与sockets相关联时streams 会遇到错误从而不能进一步处理stream data。一般情况下,错误提示了在流的一端缺失了东西,比如说远程主机的crash,正在使用的文件被删除等等。在此情况下,客户端能够做的就是将这些 错误提示给用户,尽管一个stream object在上报错误之后,在它关闭之前仍然可以查询它的状态,但是它不能再用于写或者读操作。

        当错误发生时,NSStream和NSOutputStream类通过以下几种方式发起通知:

        1,如果stream object布置在一个run loop上,那么该对象通过stream:handleEvent:消息向其delegate发送NSStreamEventErrorOccurred事件发起通知

        2,任何时候客户端可以向stream object发送streamStatus消息查询是否会返回NSStreamStatusError

        3,如果你尝试通过write:maxLength:消息向NSOutputStream对象写数据时,该消息返回值 -1,这时发生一个写错误

        一旦你检查到stream object发生错误,你可以向stream object发送streamError消息获得更加详细的信息(是一个NSError对象),然后将该错误信息通知给用户。下面的代码是布置在一个run loop上的stream object的delegate处理错误的方法:

        1. - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {  
        2.     NSLog(@"stream:handleEvent: is invoked...");  
        3.    
        4.     switch(eventCode) {  
        5.         case NSStreamEventErrorOccurred:  
        6.         {  
        7.             NSError *theError = [stream streamError];  
        8.             NSAlert *theAlert = [[NSAlert alloc] init]; // modal delegate releases  
        9.             [theAlert setMessageText:@"Error reading stream!"];  
        10.             [theAlert setInformativeText:[NSString stringWithFormat:@"Error %i: %@",  
        11.                 [theError code], [theError localizedDescription]]];  
        12.             [theAlert addButtonWithTitle:@"OK"];  
        13.             [theAlert beginSheetModalForWindow:[NSApp mainWindow]  
        14.                 modalDelegate:self  
        15.                 didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:)  
        16.                 contextInfo:nil];  
        17.             [stream close];  
        18.             [stream release];  
        19.             break;  
        20.         }  
        21.         // continued ....  
        22.     }  
        23. }
        24. 处理流错误
        对于某些错误,除了通知用户之后你可以做更多的工作。比如,如果你在进行socket连接的时候设置了SSL 安全等级,但是远程的主机没有设定,该stream object会发送一个错误,你可以释放之前的stream object并且创建一个新的不安全的套接字连接。
  • 相关阅读:
    访问者模式
    oracle触发器简单实用示例
    C#控件交互效果类(也可以用作缩小面板放大,展示更多信息)
    23种设计模式探索C#
    windows快捷操作个人记录(常用)
    C#巧妙使用关键字async/await
    学习的枚举类型,结构以及初步了解数组
    目前学习.net时间让我摸不着头脑的事情
    对C#中几个循环语句的使用,请教
    学习了用控制台显示结果
  • 原文地址:https://www.cnblogs.com/jinjiantong/p/3009226.html
Copyright © 2011-2022 走看看