在Cocoa 里面,使用 NSInputStream
实例变量,包括一些几个步骤:
1.从一个数据源创建并初始化一个 NSInputStream
对象
2.将第一步创建的流对象安排到当前的run loop中,并开启流对象。
3.处理代理对象汇报的事件
4.数据读取完毕时,dispose 流对象.
下面的介绍会详细说明上面的步骤。
准备流对象Preparing the Stream Object
使用NSInputStream
对象之前,你必须有一个数据源,这个数据源可以是文件,an NSData
object, 或者网络套接字接口 。
NSInputStream 初始化和类方法,从NSData或者文件,允许创建初始化变量,示例一从文件创建NSInputStream变量。
创建并初始化一个NSInputStream 对象
- (void)setUpStreamForFile:(NSString *)path { |
// iStream is NSInputStream instance variable |
iStream = [[NSInputStream alloc] initWithFileAtPath:path]; |
[iStream setDelegate:self]; |
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop] |
forMode:NSDefaultRunLoopMode]; |
[iStream open]; |
} |
如示例所示,创建完对象,需要设置代理,当对象安排到run loop中,并有数据流相关的事件进行汇报的时候,代理接受到stream:handleEvent:
信息 ,比如当有数据流信息需要读取的时候。
在开启数据流之前,需要发送scheduleInRunLoop:forMode:信息给数据流对象,以安排其接受数据流事件,通过这个,你可以帮助代理实现避免堵塞当没有数据进行读取的时候。当数据流在其他线程发生时,确保将数据流对象安排在对应的线程的run loop 中。你绝对不能从其他不属于当前线程的run loop获取数据流。最后,发送open 信息给数据流对象,开启数据流的输入。
Handling Stream Events
数据流开启后,你可以获取到它的状态,是否有可读取的二进制流,错误信息和以下信息:
-
streamStatus
-
hasBytesAvailable
-
streamError
返回数据流当前的状态是一个NSStreamStatus常量,是否开启,正在读取中,读取到文件的最后等等。返回的错误是一个
NSError
对象,包含错误信息。
更重要的是,一旦数据流开启,它一直发送 stream:handleEvent:信息给它的代理直到其访问到文件的最后,这些信息包含一个参数,是一个
NSStreamEvent常量
,标识着事件的类型。对于NSInputStream
对象,常用的事件类型为 are NSStreamEventOpenCompleted
, NSStreamEventHasBytesAvailable
, and NSStreamEventEndEncountered
. 代理最感兴趣的是NSStreamEventHasBytesAvailable
事件.
示例2 介绍了一个很好的处理该事件的方法
Handling a bytes-available event
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode { |
|
switch(eventCode) { |
case NSStreamEventHasBytesAvailable: |
{ |
if(!_data) { |
_data = [[NSMutableData data] retain]; |
} |
uint8_t buf[1024]; |
unsigned int len = 0; |
len = [(NSInputStream *)stream read:buf maxLength:1024]; |
if(len) { |
[_data appendBytes:(const void *)buf length:len]; |
// bytesRead is an instance variable of type NSNumber. |
[bytesRead setIntValue:[bytesRead intValue]+len]; |
} else { |
NSLog(@"no buffer!"); |
} |
break; |
} |
// continued |
在这个代理方法的实现中,stream:handleEvent:,使用了
switch 语句,区分NSStreamEvent
变量,如果变量是 NSStreamEventHasBytesAvailable
,代理首先创建一个 NSMutableData
对象(_data
) 来保存获取到的二进制流,然后声明一个一定大小的缓冲区buffer(这里是1024字节),然后调用数据流对象的方法 read:maxLength:
, 通过这个方法可以填充缓冲区,如果读取操作成功获取到数据流,代理会将数据流添加给 NSMutableData对象
.
这里没有统一的规定关于一次性读取多少字节的数据,甚至可以一次性读取全部的数据,这取决于数据流的大小,设备和套接字接口的特点。最好的方法是使用合理的缓冲区大小,比如512字节,1M,或者4M.
如果数据流对象在处理数据的时候发生错误,会停止处理,并告知代理aNSStreamEventErrorOccurred
.代理需要按照“Handling Stream Errors.”处理错误。
Disposing of the Stream Object
当数据流对象访问到数据的最后的时候,需要发送给代理a NSStreamEventEndEncountered
事件通过代理方法stream:handleEvent:发送
信息. 代理需要按照创建代理相反的方法处理数据流对象。换句话说,就是关闭数据流,从 run loop中移除,最后 release 。
示例3 展示了如何处理
Closing and releasing the NSInputStream object
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode |
{ |
switch(eventCode) { |
case NSStreamEventEndEncountered: |
{ |
[stream close]; |
[stream removeFromRunLoop:[NSRunLoop currentRunLoop] |
forMode:NSDefaultRunLoopMode]; |
[stream release]; |
stream = nil; // stream is ivar, so reinit it |
break; |
} |
// continued ... |
} |
} |