如何正确处理 Swift 2.0 中的 NSFileHandle 异常?

How to properly handle NSFileHandle exceptions in Swift 2.0?

首先,我是 iOS 和 Swift 的新手,有 Android/Java 编程背景。所以对我来说,在尝试写入文件时捕获异常的想法是第二天性,以防缺少 space、文件权限问题或文件可能发生的任何其他事情(并且已经发生,在我的经验中)。我也明白 Swift 中的异常与 Android/Java 中的异常不同,所以这不是我在这里要问的。

我正在尝试使用 NSFileHandle 附加到文件,如下所示:

let fileHandle: NSFileHandle? = NSFileHandle(forUpdatingAtPath: filename)
if fileHandle == nil {
    //print message showing failure
} else {
    let fileString = "print me to file"
    let data = fileString.dataUsingEncoding(NSUTF8StringEncoding)
    fileHandle?.seekToEndOfFile() 
    fileHandle?.writeData(data!)
}

但是,seekToEndOfFile()writeData() 函数都表明它们抛出了某种异常:

This method raises an exception if the file descriptor is closed or is not valid, if the receiver represents an unconnected pipe or socket endpoint, if no free space is left on the file system, or if any other writing error occurs. - Apple Documentation for writeData()

那么在 Swift 2.0 中处理这个问题的正确方法是什么?我已经阅读了链接 Error-Handling in Swift-Language, try-catch exceptions in Swift, NSFileHandle writeData: exception handling, , and How to catch an exception in Swift,但其中 none 直接回答了我的问题。我确实读过一些关于在 Swift 代码中使用 objective-C 的内容,但由于我是 iOS 的新手,我不知道这个方法是什么,而且似乎无法在任何地方找到它。我还尝试了新的 Swift 2.0 do-catch 块,但他们没有意识到 NSFileHandle 方法会抛出任何类型的错误,这很可能是因为函数文档没有 throw 关键字。

我知道如果应用程序用完 space 或其他什么,我可以让应用程序崩溃,但由于应用程序稍后可能会发布到应用程序商店,我不希望这样。那么我该如何使用 Swift 2.0 方式做到这一点?

编辑: 目前这是一个只有 Swift 代码的项目,所以即使在 Objective-C 中似乎有办法做到这一点,我不知道如何融合两者。

seekToEndOfFile()writeData() 未标记为 throws(它们不会抛出可以用 do-try-catch 捕获的 NSError 对象block),也就是说在Swift的当前状态下,他们提出的NSException不能是"caught".

如果您正在处理一个 Swift 项目,您可以创建一个 Objective-C class 来实现您的 NSFileHandle 方法来捕获 NSException s(如 this question),否则你就不走运了。

这个可以不使用Objective C代码就可以实现,这里有一个完整的例子。

class SomeClass: NSObject {
    static func appendString(string: String, filename: String) -> Bool {
        guard let fileHandle = NSFileHandle(forUpdatingAtPath: filename) else { return false }
        guard let data = string.dataUsingEncoding(NSUTF8StringEncoding) else { return false }

        // will cause seekToEndOfFile to throw an excpetion
        fileHandle.closeFile()

        SomeClass.startHandlingExceptions()
        fileHandle.seekToEndOfFile()
        fileHandle.writeData(data)
        SomeClass.stopHandlingExceptions()

        return true
    }

    static var existingHandler: (@convention(c) NSException -> Void)?
    static func startHandlingExceptions() {
        SomeClass.existingHandler = NSGetUncaughtExceptionHandler()
        NSSetUncaughtExceptionHandler({ exception in
            print("exception: \(exception))")
            SomeClass.existingHandler?(exception)
        })
    }

    static func stopHandlingExceptions() {
        NSSetUncaughtExceptionHandler(SomeClass.existingHandler)
        SomeClass.existingHandler = nil
    }
}

调用 SomeClass.appendString("add me to file", filename:"/some/file/path.txt") 到 运行 它。

第二个(可恢复的)解决方案是创建一个非常简单的 ObjectiveC++ 函数,它接受一个块和 returns 一个异常。

创建一个名为:ExceptionCatcher.h 的文件并将其添加到您的桥接 header 中(如果您还没有,Xcode 会提示您创建一个)

//
//  ExceptionCatcher.h
//

#import <Foundation/Foundation.h>

NS_INLINE NSException * _Nullable tryBlock(void(^_Nonnull tryBlock)(void)) {
    @try {
        tryBlock();
    }
    @catch (NSException *exception) {
        return exception;
    }
    return nil;
}

使用这个助手非常简单,我已经修改了上面的代码来使用它。

func appendString(string: String, filename: String) -> Bool {
    guard let fileHandle = NSFileHandle(forUpdatingAtPath: filename) else { return false }
    guard let data = string.dataUsingEncoding(NSUTF8StringEncoding) else { return false }

    // will cause seekToEndOfFile to throw an excpetion
    fileHandle.closeFile()

    let exception = tryBlock {
        fileHandle.seekToEndOfFile()
        fileHandle.writeData(data)
    }
    print("exception: \(exception)")

    return exception == nil
}