I was wondering what's the best way to make NSOutputStream
redirect to standard output. The technique I am using right now is to use an output stream that writes to memory, get its data and print that to stdout:
开发者_StackOverflow NSOutputStream *stream = [[NSOutputStream alloc] initToMemory];
[stream open];
// calls to stream's -write:maxLengh:
NSData *data = [stream propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
printf("%s", [string UTF8String]);
[stream close];
Is there a better way to achieve this? Specifically, I am not happy about two things with this approach:
The need for extra memory for data that is written to stream
This stream is not reusable -- after I have retrieved data from this stream via
[stream propertyForKey:NSStreamDataWrittenToMemoryStreamKey]
, the stream is not "reset" i.e. I want subsequent calls to this method to give me only new data, but its not the case. This means that I have to create a new NSOutputStream after every time I write to stdout.
It doesn't appear there's a built-in way to do this. If you can use NSFileHandle
instead of NSOutputStream
, you can use [NSFileHandle fileHandleWithStandardOutput]
. If you have to use NSOutputStream
, try something like this:
// untested!
@interface FileHandleOutputStream : NSOutputStream
+ (FileHandleOutputStream *)outputStreamWithFileHandle:(NSFileHandle *)fileHandle;
- (id)initWithFileHandle:(NSFileHandle *)fileHandle;
@end
@implementation FileHandleOutputStream {
NSFileHandle *_fileHandle;
}
+ (FileHandleOutputStream *)outputStreamWithFileHandle:(NSFileHandle *)fileHandle {
return [[[self alloc] initWithFileHandle:fileHandle] autorelease];
}
- (id)initWithFileHandle:(NSFileHandle *)fileHandle {
if (self = [super init]) {
_fileHandle = [fileHandle retain];
}
return self;
}
- (void)dealloc {
[_fileHandle release];
[super dealloc];
}
- (BOOL)hasSpaceAvailable {
return YES;
}
- (NSInteger)write:(const uint8_t *)buffer maxLength:(NSUInteger)length {
[_fileHandle writeData:[NSData dataWithBytesNoCopy:buffer
length:length
freeWhenDone:NO]];
return length;
}
Now use
FileHandleOutputStream *myStrem = [FileHandleOutputStream outputStreamWithFileHandle:
[NSFileHandle fileHandleWithStandardOutput]];
There's no need for Cocoa to provide its own constructor here because stdout is available as a "file." This doesn't seem to be documented, unfortunately, but works very well:
NSOutputStream *outputStream =
[NSOutputStream outputStreamToFileAtPath:@"/dev/stdout"
append:NO];
You can do the same thing with stderr as well (or use a NSInputStream for stdin).
You can also open a NSOutputStream to stdout using "/dev/tty" as the file. ("/dev/tty" is the Unix designation for standard input and output.)
Thus you can create a NSOutputStream to standard out with:
NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath: @"/dev/tty"
append: NO];
A drawback is that this won't work if you run your program within Xcode; you'll have to run it in a terminal window.
精彩评论