我的沙盒Mac应用程序(显然)泄漏文件资源(句柄?)在向它添加文件(并将其中一些文件转换为不同的格式)时,它将在-[NSFileManager copyItemAtPath:toPath:error:]
上失败,并出现以下底层错误:
NSUnderlyingError=0x600000440a50 "The operation couldn’t be completed. Too many open files"
我可以可靠地复制这个Xcode外部(与调试构建),但从来没有在内部。看起来某个地方的文件一直在打开,但从未关闭。每次当我删除同一个文件夹时,它都会发生在同一个文件上,或者当我取出那个文件时,它会发生在下一个文件上。我记录了我能想到的在NSFileManager
之外做文件I/O的每个地方,看起来我所有的开放调用都与关闭平衡,就像我在NSURLs
上启动和停止安全资源访问的调用一样。
- 为什么这不会发生在Xcode内部?
- 如何追踪不平衡呼叫发生的位置?
- 错误是否可能是转移注意力?
我试着在Xcode以外的仪器中运行文件活动预设,并能够再现其中的错误,但它看起来不像有任何方法可以从所使用的仪器(文件活动,读/写,文件属性,目录I/O)中获得我需要的信息。
我最终通过仪器找到了根本原因。我根据FD列对File Attributes instrument的事件列表进行了排序,并且能够看到对于相同的文件描述符有许多"打开"事件(有些对于相同的文件和不同的描述符),没有任何"关闭"事件。我可以追踪到我使用的一个库中的违规代码。我在单元测试中复制了它,使用这种方法并比较了一些代码运行前后的情况:
- (NSInteger)numberOfOpenFileHandles {
int pid = [[NSProcessInfo processInfo] processIdentifier];
NSPipe *pipe = [NSPipe pipe];
NSFileHandle *file = pipe.fileHandleForReading;
NSTask *task = [[NSTask alloc] init];
task.launchPath = @"/usr/sbin/lsof";
task.arguments = @[@"-P", @"-n", @"-p", [NSString stringWithFormat:@"%d", pid]];
task.standardOutput = pipe;
[task launch];
NSData *data = [file readDataToEndOfFile];
[file closeFile];
NSString *lsofOutput = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
// NSLog(@"LSOF:n%@", lsofOutput);
return [lsofOutput componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]].count;
}