web-dev-qa-db-ja.com

Swift 2.0でNSFileHandle例外を適切に処理する方法は?

まず第一に、私はiOSに不慣れで、Swiftであり、Android/Javaプログラミングのバックグラウンドを持っています。したがって、ファイルへの書き込みの試みから例外をキャッチするという考えは2番目です。性質上、スペース不足、ファイルパーミッションの問題、またはファイルに発生する可能性のあるその他の問題が発生した場合(私の経験では発生しています)、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()関数はどちらも、ある種の例外をスローすることを示しています。

このメソッドは、ファイル記述子が閉じているか無効である場合、レシーバーが接続されていないパイプまたはソケットエンドポイントを表す場合、ファイルシステムに空き領域が残っていない場合、またはその他の書き込みエラーが発生した場合に例外を発生させます。 -Apple writeData()のドキュメント

それで、これをSwift 2.0で処理する適切な方法は何ですか?私はリンクを読みました Swift-Languageでのエラー処理try-catch Swiftの例外NSFileHandle writeData:例外処理Swift 2.0例外処理 、および Swiftで例外をキャッチする方法 、ただしなしそのうちの1つが私の質問に直接答えています。Swiftコードでobjective-Cを使用することについて何か読んだことがありますが、iOSを初めて使用するため、この方法が何であるかわかりません。どこにも見つからないようです。新しいSwift 2.0 _do-catch_ブロックも試しましたが、NSFileHandleメソッドでエラーがスローされていることを認識していません。関数のドキュメントにthrowキーワードがないため、おそらくそうです。

スペースが足りなくなった場合などにアプリをクラッシュさせる可能性があることは承知していますが、アプリは後でアプリストアにリリースされる可能性があるため、それは望ましくありません。では、これをSwift 2.0の方法で行うにはどうすればよいですか?

EDIT:これは現在、Swiftコードのみのプロジェクトであるため、これはObjective-Cで、2つをブレンドする方法がわかりません。

17
mirage

2番目の(回復可能な)解決策は、ブロックを受け取り、例外を返す非常に単純なObjectiveC++関数を作成することです。

exceptionCatcher.hというタイトルのファイルを作成し、ブリッジヘッダーにインポートを追加します(まだ作成していない場合、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
}
25
Casey

これはcan ObjectiveCコードを使用せずに実現できます。完全な例を次に示します。

_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")を呼び出して実行します。

2
Casey

seekToEndOfFile()およびwriteData()throwsとしてマークされていません(do-try-catchブロックでキャッチできるNSErrorオブジェクトをスローしません)、つまり、Swiftの現在の状態では、それらによって発生したNSExceptionsを「キャッチ」することはできません。

Swiftプロジェクトで作業している場合は、NSFileHandlesをキャッチするNSExceptionメソッドを実装するObjective-Cクラスを作成できます(-のように この質問 )、しかしそうでなければあなたは運が悪いです。

1
JAL