對於 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];
}
- (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)];
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)];
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。 下面是一段相關代碼
|
CFStreamClientContext ctx = {0, (__bridge void *)(self), NULL, NULL, NULL};
CFOptionFlags registeredEvents = (kCFStreamEventHasBytesAvailable | kCFStreamEventEndEncountered | kCFStreamEventErrorOccurred);
CFReadStreamRef readStream;
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef)host, (UInt32)port, &readStream, NULL);
if (CFReadStreamSetClient(readStream, registeredEvents, socketCallback, &ctx)) {
CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
}
else {
[self networkFailedWithErrorMessage:@"Failed to assign callback method"];
return;
}
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;
}
|