1. 程式人生 > >ios開發之向輸出流寫資料

ios開發之向輸出流寫資料

使用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 寫入資料。

[cpp] view plaincopyprint?
  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事件的一種方法:

[cpp] view plaincopyprint?
  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的工作:

[cpp] view plaincopyprint?
  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物件中。