我通过编写一个将文件描述符重定向到文件并能够重置此重定向的实用程序类解决了这个问题:
@interface IORedirector : NSObject
- (instancetype)initWithFileDescriptor:(int)fileDescriptor targetPath:(NSURL *)fileURL flags:(int)oflags mode:(mode_t)mode;
- (void)reset;
@end
@implementation IORedirector
{
int _fileDescriptor;
int _savedFileDescriptor;
}
- (instancetype)initWithFileDescriptor:(int)fileDescriptor targetPath:(NSURL *)fileURL flags:(int)oflags mode:(mode_t)mode
{
_savedFileDescriptor = dup(fileDescriptor);
if (_savedFileDescriptor == -1) {
NSLog(@"Could not save file descriptor %d: %s", fileDescriptor, strerror(errno));
return nil;
}
int tempFD = open(fileURL.path.fileSystemRepresentation, oflags, mode);
if (tempFD == -1) {
NSLog(@"Could not open %@: %s", fileURL.path, strerror(errno));
close(_savedFileDescriptor);
return nil;
}
if (close(fileDescriptor) == -1) {
NSLog(@"Closing file descriptor %d failed: %s", fileDescriptor, strerror(errno));
close(_savedFileDescriptor);
close(tempFD);
return nil;
}
if (dup2(tempFD, fileDescriptor) == -1) {
NSLog(@"Could not replace file descriptor %d with new one for %@: %s", fileDescriptor, fileURL.path, strerror(errno));
close(_savedFileDescriptor);
close(tempFD);
return nil;
}
_fileDescriptor = fileDescriptor;
close(tempFD);
return self;
}
- (void)dealloc
{
[self reset];
}
- (void)reset
{
if (_savedFileDescriptor == -1) return;
if (close(_fileDescriptor) == -1) {
NSLog(@"Closing file descriptor %d failed: %s", _fileDescriptor, strerror(errno));
return;
}
if (dup2(_savedFileDescriptor, _fileDescriptor) == -1) {
NSLog(@"Could not restore file descriptor %d: %s", _fileDescriptor, strerror(errno));
return;
}
close(_savedFileDescriptor);
_savedFileDescriptor = -1;
}
@end
初始化程序要么返回一个有效实例(重定向已建立),要么返回nil。立即致电reset 恢复重定向。
初始化程序和reset 都可能失败并“丢失”原始文件描述符,但窗口很小且不太可能。这可能仅在多线程应用程序中使用此类时才相关(我不这样做),因此如果您打算在多线程环境中使用它,您需要确保一次只能运行一个初始化程序或reset使用全局互斥锁之类的。
使用这个类(当然,我有一个单独的测试用例),我可以将STDERR_FILENO 重定向到一个文件,从而捕获NSLog 的输出。这样我就可以测试未修改的日志类了。
为临时文件创建合适的路径留给读者作为练习。