为啥 FileHandle 不一致地返回“nil”

Posted

技术标签:

【中文标题】为啥 FileHandle 不一致地返回“nil”【英文标题】:Why is FileHandle inconsistently returning 'nil'为什么 FileHandle 不一致地返回“nil” 【发布时间】:2018-05-31 20:57:44 【问题描述】:

我有一个应用程序在使用 FileHandle 打开文件进行读取时不一致地返回“nil”。我在 OSX (10.13.4)、XCode 9.4、Swift 4.1

此 OSX 应用程序使用 NSOpenPanel() 来获取用户选择的文件列表。我的“模型”类代码打开这些文件以构建数据结构的集合。执行此操作的代码以这样的方式开始,每次都成功地为任何文件获取 FileHandle,并且能够从文件中读取数据。

private func getFITHeader(filename: String) 
    let file: FileHandle? = FileHandle(forReadingAtPath: filename)
    if file == nil 
        print("FITFile >>> File open failed for file \(filename)")
    
    else 
        var databuffer: Data
        databuffer = (file?.readData(ofLength: 80))!
        :
        :
    

这些文件还包含我在应用程序的另一部分处理的二进制数据块。当我为此开发代码时,出于测试目的,我暂时对与上述工作相同的文件名之一进行硬编码。但是这段代码(如下)总是抛出异常'Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value' 当它到达 fileHandle?.seek() - 出于某种原因尝试尽管代码在功能上与上述相同,但创建 FileHandle 始终返回“nil”。

@IBAction func btnProcFile(_ sender: Any) 

    var data: Data
    let filename = "/Users/johncneal/Dropbox/JN Astronomy/Astronomy/Spectroscopy/RSpec_Analyses/Gamma_Cas/20161203/Gamma Cas_065.fit"

    let fileHandle: FileHandle? = FileHandle(forReadingAtPath: filename)
    fileHandle?.seek(toFileOffset: 2880) //skip past the headers

    let dataLenToRead = 1391 * 1039 * 2
    data = (fileHandle?.readData(ofLength: dataLenToRead))!
    :
    :

第二个函数中的代码在 Playground 中可以正常工作(没有附加太多含义),而且奇怪的是,在临时添加到不同项目时也可以正常工作。可能还值得一提的是文件路径的长度似乎并不重要 - 它在短路径上的行为相同。

所以问题是 - 为什么 FileHandle 的这种行为可靠地不一致?

print()'ing 提供给 FileHandle() 的文件名表明它们在每种情况下都是相同的(见下文)。所以我对此感到困惑和沮丧 - 任何观点或解决方法都会受到赞赏。

/Users/johncneal/Dropbox/JN 天文学/天文学/光谱学/RSpec_Analyses/Gamma_Cas/20161203/Gamma Cas_065.fit

/Users/johncneal/Dropbox/JN 天文学/天文学/光谱学/RSpec_Analyses/Gamma_Cas/20161203/Gamma Cas_065.fit

【问题讨论】:

如果fileHandlenil,则fileHandle?.seek 行不能抛出该错误。使用调试器。如果fileHandle真的是nil 【参考方案1】:

FileHandle 初始值设定项命名不正确。

您应该使用FileHandle(forReadingFrom:URL) 而不是FileHandle(forReadingAtPath:String)。前者是较新的 API,它会抛出错误而不是返回 nil。您可以使用抛出的错误来查看它失败的原因,并且保证您的变量不为零。

例如:

@IBAction func btnProcFile(_ sender: Any) 

    do 
        let fileUrl = URL(fileURLWithPath:"/Users/johncneal/Dropbox/JN Astronomy/Astronomy/Spectroscopy/RSpec_Analyses/Gamma_Cas/20161203/Gamma Cas_065.fit")

        let fileHandle = try FileHandle(forReadingFrom: fileUrl)
        fileHandle.seek(toFileOffset: 2880) //skip past the headers

        let dataLenToRead = 1391 * 1039 * 2
        let data: Data = fileHandle.readData(ofLength: dataLenToRead)
        // etc...
     catch let error as NSError 
        print("FITFile >>> File open failed: \(error)")
        NSApp.presentError(error)
    


【讨论】:

虽然这都是很好的建议,但请记住,如果 fileHandlenil,则 OP 的代码不会导致所述错误,因为在调用 seek 时使用了可选链接。 【参考方案2】:

找到答案 - 沙盒!!

Darren - 巧合的是,我确实查看了基于 URL 的路由,发现它“抛出”了一些适当的错误报告。低,他们报告说我没有文件的权限(这最初让我感到惊讶,因为我显然是我的 Mac 上的管理员,所有文件都是本地的,并且在我的用户名下。

我做了更多的研究。这篇文章 - https://forums.developer.apple.com/thread/96062 很快揭示了它的沙盒问题:-) 看起来最近版本的 XCode 在“权利”中打开了它。该帖子还指出 NSOpenPanel FileOpen 对话框返回“Security scoped urls”。起初我认为这解释了为什么第一个函数中的代码有效,但我并不完全相信,因为我只是将 url.path 属性提供给 FileHandle。

但是,在权利中关闭沙盒可以让一切正常。是的,我知道这不是长期做的正确事情(或者如果我想让它进入 App Store),所以我会检查正确的方法来做到这一点。至少我现在可以继续 - 感谢您的意见。

【讨论】:

以上是关于为啥 FileHandle 不一致地返回“nil”的主要内容,如果未能解决你的问题,请参考以下文章

为啥带赋值的三元运算符不返回预期的输出?

为啥 NSFormatter dateFromString 偶尔会返回 nil?

为啥 MKTileOverlay initWithURLTemplate: 模板返回 nil?

为啥我的 tableView 变量返回 nil?

为啥我的 json 响应对象返回 nil?

为啥 uitextfield 在成为第一响应者时从不返回 nil?