NSInputStream 和 NSOutputStream

對於 NSInputStream 和 NSOutputStream 一直沒怎麼搞清楚,今天抽一些時間在此記錄一下!

NSInputStream 與 NSOutputStream 都繼承於 NSStream, NSStream 是一個抽象的基類, 規定了Stream共有的一些行爲…

什麼是Stream

Stream翻譯成爲流,它是對我們讀寫文件的一個抽象。 你可以這樣想象,當你讀文件和寫文件的時候,文件的內容就像水流一樣嘩嘩的 像你流過來或者流給別人,這樣豈不是很爽。 而Stream就幫我們做了這樣的事情, 實際上,它是把文件的內容,一小段一小段的讀出或 寫入,來到達這樣的效果

NSStream

NSStream 是Cocoa平臺下對流這個概念的實現類, NSInputStream 和 NSOutputStream 則是它的兩個子類,分別對應了讀文件和 寫文件。

NSInputStream

NSInputStream 對應的是讀文件,所以要記住它是要將文件的內容讀到內存(你聲明的一段buffer)裏, 下面一段是測試代碼

 
- (void)doTestInputStream {
  NSString *path = @"/Users/usr/Desktop/stream.txt";
  
  NSInputStream *readStream = [[NSInputStream alloc]initWithFileAtPath:path];
  [readStream setDelegate:self];
  
  [readStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
  [readStream open]; //調用open開始讀文件
}

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
  switch (eventCode) {
      case NSStreamEventHasBytesAvailable:{
          
          uint8_t buf[1024];
          
          NSInputStream *reads = (NSInputStream *)aStream;
          NSInteger blength = [reads read:buf maxLength:sizeof(buf)]; //把流的數據放入buffer
          NSData *data = [NSData dataWithBytes:(void *)buf length:blength];
          
          NSString *string = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
          NSLog(@"%@",string);
      }
          break;
                  

      //錯誤和無事件處理
      case NSStreamEventErrorOccurred:{
          
      }
          break;
      case NSStreamEventNone:
          break;
      
      //打開完成
      case NSStreamEventOpenCompleted: {
          NSLog(@"NSStreamEventOpenCompleted");
      }
          break;
          
      default:
          break;
  }
}

@end

NSOutputStream

NSOutputStream 對應的是寫文件,它是要將已存在的內存(buffer)裏的數據寫入文件, 下面同樣一段是測試代碼

 
- (NSData *)dataWillWrite {
  static  NSData *data = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
      data = [NSData dataWithContentsOfFile:@"/Users/usr/Desktop/stream.txt"];
  });
  
  return data;
}

- (void)doTestOutputStream {
  NSString *path = @"/Users/usr/Desktop/stream-write.txt";
  
  NSOutputStream *writeStream = [[NSOutputStream alloc] initToFileAtPath:path append:YES];
  [writeStream setDelegate:self];
  
  [writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
  [writeStream open];
}

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
  switch (eventCode) {
      case NSStreamEventHasSpaceAvailable: {
          
          NSInteger bufSize = 5;
          uint8_t buf[bufSize];
          
          if (self.location + bufSize > [self dataWillWrite].length) {
              [[self dataWillWrite] getBytes:buf
                                       range:NSMakeRange(self.location, self.location + bufSize - [self dataWillWrite].length)];
          }
          else {
              [[self dataWillWrite] getBytes:buf range:NSMakeRange(self.location, bufSize)];
          }
          
          NSOutputStream *writeStream = (NSOutputStream *)aStream;
          [writeStream write:buf maxLength:sizeof(buf)]; //把buffer裏的數據,寫入文件
          
          self.location += bufSize;
          if (self.location >= [[self dataWillWrite] length] ) { //寫完後關閉流
              [aStream close];
          }
          
      }
          break;
          
      case NSStreamEventEndEncountered: {
          [aStream close];
      }
          break;
      
      //錯誤和無事件處理
      case NSStreamEventErrorOccurred:{
          
      }
          break;
      case NSStreamEventNone:
          break;
      
      //打開完成
      case NSStreamEventOpenCompleted: {
          NSLog(@"NSStreamEventOpenCompleted");
          
          
      }
          break;
          
      default:
          break;
  }
}

用途

NSInputStream 和 NSOutputStream 常用與網絡傳輸中,比如要將一個很大的文件傳送給服務器,那麼NSInputStream這時候是 很好的選擇, 我們可以查看到 NSURLRequest 有一個屬性叫HTTPBodyStream, 這時只要設置好一個NSInputStream的實例就可以 了,最大的好處就是可以節省我們很多的內存。

另外要說明的是,NSInputStream 和 NSOutputStream其實是對 CoreFoundation 層對應的CFReadStreamRef 和 CFWriteStreamRef 的高層抽象。在使用CFNetwork時,常常會使用到CFReadStreamRef 與 CFWriteStreamRef。 下面是一段相關代碼

 
 // Keep a reference to self to use for controller callbacks
    //
  CFStreamClientContext ctx = {0, (__bridge void *)(self), NULL, NULL, NULL};
  
  // Get callbacks for stream data, stream end, and any errors
    //
  CFOptionFlags registeredEvents = (kCFStreamEventHasBytesAvailable | kCFStreamEventEndEncountered | kCFStreamEventErrorOccurred);
  
  // Create a read-only socket
    //
  CFReadStreamRef readStream;
  CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef)host, (UInt32)port, &readStream, NULL);
  
  // Schedule the stream on the run loop to enable callbacks
    //
  if (CFReadStreamSetClient(readStream, registeredEvents, socketCallback, &ctx)) {
      CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
      
  }
    else {
        [self networkFailedWithErrorMessage:@"Failed to assign callback method"];
      return;
  }
  
  // Open the stream for reading
    //
  if (CFReadStreamOpen(readStream) == NO) {
        [self networkFailedWithErrorMessage:@"Failed to open read stream"];
      
      return;
  }
  
  CFErrorRef error = CFReadStreamCopyError(readStream);
  if (error != NULL) {
      if (CFErrorGetCode(error) != 0) {
          NSString * errorInfo = [NSString stringWithFormat:@"Failed to connect stream; error '%@' (code %ld)", (__bridge NSString*)CFErrorGetDomain(error), CFErrorGetCode(error)];
            [self networkFailedWithErrorMessage:errorInfo];
      }
      
      CFRelease(error);
      
      return;
  }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章