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 寫入資料。
- - (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];
- }
當你在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?- - (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的工作:
[cpp] view plaincopyprint?- - (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 ...
- }
- }