私はSwiftをあまり読みませんでしたが、例外はないことに気づきました。では、Swiftでエラー処理をどのように行うのでしょうか。誰かがエラー処理に関連する何かを見つけましたか?
私はSwiftをあまり読みませんでしたが、例外はないことに気づきました。では、Swiftでエラー処理をどのように行うのでしょうか。誰かがエラー処理に関連する何かを見つけましたか?
回答:
Swift 2では、例外に多少似ていますが詳細が異なる新しいエラー処理メカニズムがあるため、状況が少し変更されています。
関数/メソッドがエラーをスローする可能性があることを示したい場合は、throws
このようなキーワードを含める必要があります
func summonDefaultDragon() throws -> Dragon
注:関数が実際にスローできるエラーのタイプの仕様はありません。この宣言は、関数がErrorTypeを実装する任意のタイプのインスタンスをスローできるか、まったくスローしないことを示しています。
関数を呼び出すには、次のようにtryキーワードを使用する必要があります
try summonDefaultDragon()
この行は通常、このようなdo-catchブロックが存在する必要があります
do {
let dragon = try summonDefaultDragon()
} catch DragonError.dragonIsMissing {
// Some specific-case error-handling
} catch DragonError.notEnoughMana(let manaRequired) {
// Other specific-case error-handlng
} catch {
// Catch all error-handling
}
注:catch句はSwiftパターンマッチングのすべての強力な機能を使用するため、ここでは非常に柔軟です。
それ自体がthrows
キーワードでマークされている関数からスロー関数を呼び出している場合は、エラーを伝播することを決定できます。
func fulfill(quest: Quest) throws {
let dragon = try summonDefaultDragon()
quest.ride(dragon)
}
または、次を使用してスロー関数を呼び出すこともできますtry?
。
let dragonOrNil = try? summonDefaultDragon()
この方法では、エラーが発生した場合に戻り値またはnilを取得します。この方法を使用すると、エラーオブジェクトを取得できません。
つまり、次のtry?
ような便利なステートメントと組み合わせることができます。
if let dragon = try? summonDefaultDragon()
または
guard let dragon = try? summonDefaultDragon() else { ... }
最後に、エラーが実際には発生しないことを確認して(たとえば、すでにチェック済みであることを前提としているため)、次のtry!
キーワードを使用できます。
let dragon = try! summonDefaultDragon()
関数が実際にエラーをスローすると、アプリケーションでランタイムエラーが発生し、アプリケーションが終了します。
エラーをスローするには、次のようにthrowキーワードを使用します
throw DragonError.dragonIsMissing
ErrorType
プロトコルに準拠したものは何でもスローできます。初心者のNSError
場合、このプロトコルに準拠していますが、おそらく次のようなErrorType
追加のデータを使用して、複数の関連エラーをグループ化できる列挙型ベースにしたいと思うでしょう。
enum DragonError: ErrorType {
case dragonIsMissing
case notEnoughMana(requiredMana: Int)
...
}
新しいSwift 2&3エラーメカニズムとJava / C#/ C ++スタイルの例外の主な違いは次のとおりです。
do-catch
+ try
+ defer
従来のtry-catch-finally
構文。do-catch
あなたがにObjCを使用する必要があり、そのためにブロックは、任意のNSExceptionをキャッチし、その逆ではないでしょう。NSError
、false
(Bool
関数を返す場合)またはnil
(AnyObject
関数を返す場合)を返しNSErrorPointer
、エラーの詳細を渡すというCocoa メソッドの規則と互換性があります。エラー処理を容易にするための追加のsyntatic-sugarとして、さらに2つの概念があります。
defer
キーワード)。これにより、Java / C#/ etcの最終ブロックと同じ効果を達成できます。guard
通常のエラーチェック/シグナリングコードよりも少ないif / elseコードを記述できるガードステートメント(キーワードを使用)。ランタイムエラー:
Leandrosがランタイムエラー(ネットワーク接続の問題、データの解析、ファイルのオープンなど)を処理することを提案しているNSError
ように、Foundation、AppKit、UIKitなどがこの方法でエラーを報告するため、ObjCで行ったように使用する必要があります。つまり、言語のことよりもフレームワークのことです。
使用されている別の頻繁なパターンは、AFNetworkingのようなセパレーター成功/失敗ブロックです。
var sessionManager = AFHTTPSessionManager(baseURL: NSURL(string: "yavin4.yavin.planets"))
sessionManager.HEAD("/api/destoryDeathStar", parameters: xwingSquad,
success: { (NSURLSessionDataTask) -> Void in
println("Success")
},
failure:{ (NSURLSessionDataTask, NSError) -> Void in
println("Failure")
})
それでも、障害ブロックは頻繁にNSError
インスタンスを受け取り、エラーを説明します。
プログラマエラー:
プログラマーエラー(配列要素の範囲外アクセス、関数呼び出しに渡される無効な引数など)の場合、ObjCで例外を使用しました。スウィフト言語は、例外のいずれかの言語サポートを持っていないようです(のようなthrow
、catch
などのキーワード)。ただし、ドキュメントに示されているように、ObjCと同じランタイムで実行されているため、次のようにスローすることができますNSExceptions
。
NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()
ObjCコードで例外をキャッチすることを選択しても、純粋なSwiftではそれらをキャッチできません。
問題は、プログラマーのエラーに対して例外をスローすべきか、それともAppleが言語ガイドで提案しているようにアサーションを使用すべきかどうかです。
fatalError(...)
も同じです。
2015年6月9日更新-非常に重要
スウィフト2.0が付属していますtry
、throw
と、catch
キーワードと最もエキサイティングです。
Swiftは、エラーを生成するObjective-Cメソッドを、Swiftのネイティブエラー処理機能に従ってエラーをスローするメソッドに自動的に変換します。
注:デリゲートメソッドや、NSErrorオブジェクト引数を持つ完了ハンドラーを取るメソッドなど、エラーを消費するメソッドは、Swiftによってインポートされたときにスローされるメソッドにはなりません。
抜粋:Apple Inc.「CocoaおよびObjective-CでのSwiftの使用(Swift 2プレリリース)」iBooks。
例:(本から)
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *URL = [NSURL fileURLWithPath:@"/path/to/file"];
NSError *error = nil;
BOOL success = [fileManager removeItemAtURL:URL error:&error];
if (!success && error){
NSLog(@"Error: %@", error.domain);
}
Swiftで同等のものは次のようになります。
let fileManager = NSFileManager.defaultManager()
let URL = NSURL.fileURLWithPath("path/to/file")
do {
try fileManager.removeItemAtURL(URL)
} catch let error as NSError {
print ("Error: \(error.domain)")
}
エラーを投げる:
*errorPtr = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotOpenFile userInfo: nil]
呼び出し元に自動的に伝達されます:
throw NSError(domain: NSURLErrorDomain, code: NSURLErrorCannotOpenFile, userInfo: nil)
Appleの本、Swiftプログラミング言語によると、エラーは列挙型を使用して処理する必要があるようです。
これが本の例です。
enum ServerResponse {
case Result(String, String)
case Error(String)
}
let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")
switch success {
case let .Result(sunrise, sunset):
let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
let serverResponse = "Failure... \(error)"
}
出典:Apple Inc.「The Swift Programming Language」iBooks。https://itun.es/br/jEUH0.l
更新
Appleのニュースブック、「CocoaおよびObjective-CでのSwiftの使用」から。Swift言語を使用してもランタイム例外は発生しないため、try-catchはありません。代わりに、オプションのチェーンを使用します。
これが本の一部です。
たとえば、以下のコードリストでは、lengthプロパティとcharacterAtIndex:メソッドがNSDateオブジェクトに存在しないため、1行目と2行目は実行されません。myLength定数はオプションのIntであると推定され、nilに設定されます。3行目に示すように、if-letステートメントを使用して、オブジェクトが応答しない可能性のあるメソッドの結果を条件付きでアンラップすることもできます。
let myLength = myObject.length?
let myChar = myObject.characterAtIndex?(5)
if let fifthCharacter = myObject.characterAtIndex(5) {
println("Found \(fifthCharacter) at index 5")
}
抜粋:Apple Inc.「CocoaおよびObjective-CでのSwiftの使用」iBooks。https://itun.es/br/1u3-0.l
また、Objective-C(NSErrorオブジェクト)のココアエラーパターンを使用することも推奨されています。
Swiftのエラー報告は、Objective-Cと同じパターンに従いますが、オプションの戻り値を提供するという利点もあります。最も単純なケースでは、関数からBool値を返し、それが成功したかどうかを示します。エラーの理由を報告する必要がある場合は、関数にNSErrorPointerタイプのNSError出力パラメーターを追加できます。この型は、Objective-CのNSError **とほぼ同じですが、追加のメモリ安全性とオプションの型指定があります。次のコードリストに示すように、プレフィックス&演算子を使用して、オプションのNSErrorタイプへの参照をNSErrorPointerオブジェクトとして渡すことができます。
var writeError : NSError?
let written = myString.writeToFile(path, atomically: false,
encoding: NSUTF8StringEncoding,
error: &writeError)
if !written {
if let error = writeError {
println("write failure: \(error.localizedDescription)")
}
}
抜粋:Apple Inc.「CocoaおよびObjective-CでのSwiftの使用」iBooks。https://itun.es/br/1u3-0.l
Objective-Cのアプローチと同様に、Swiftには例外はありません。
開発では、を使用assert
して、表示される可能性があるエラーをキャッチし、本番環境に移行する前に修正する必要があります。
従来のNSError
アプローチは変更されず、が送信されるが送信されますNSErrorPointer
。
簡単な例:
var error: NSError?
var contents = NSFileManager.defaultManager().contentsOfDirectoryAtPath("/Users/leandros", error: &error)
if let error = error {
println("An error occurred \(error)")
} else {
println("Contents: \(contents)")
}
f();g();
がf(&err);if(err) return;g(&err);if(err) return;
最初の月になるように、それからちょうどそれになるf(nil);g(nil);hopeToGetHereAlive();
推奨される「Swift Way」は次のとおりです。
func write(path: String)(#error: NSErrorPointer) -> Bool { // Useful to curry error parameter for retrying (see below)!
return "Hello!".writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: error)
}
var writeError: NSError?
let written = write("~/Error1")(error: &writeError)
if !written {
println("write failure 1: \(writeError!.localizedDescription)")
// assert(false) // Terminate program
}
ただし、最後にエラー処理を別のブロックに移動するため、追跡が容易であるため、try / catchを使用します。この配置は、「ゴールデンパス」と呼ばれることもあります。ラッキーでこれをクロージャーで行うことができます:
TryBool {
write("~/Error2")(error: $0) // The code to try
}.catch {
println("write failure 2: \($0!.localizedDescription)") // Report failure
// assert(false) // Terminate program
}
また、再試行機能を追加するのも簡単です。
TryBool {
write("~/Error3")(error: $0) // The code to try
}.retry {
println("write failure 3 on try \($1 + 1): \($0!.localizedDescription)")
return write("~/Error3r") // The code to retry
}.catch {
println("write failure 3 catch: \($0!.localizedDescription)") // Report failure
// assert(false) // Terminate program
}
TryBoolのリストは次のとおりです。
class TryBool {
typealias Tryee = NSErrorPointer -> Bool
typealias Catchee = NSError? -> ()
typealias Retryee = (NSError?, UInt) -> Tryee
private var tryee: Tryee
private var retries: UInt = 0
private var retryee: Retryee?
init(tryee: Tryee) {
self.tryee = tryee
}
func retry(retries: UInt, retryee: Retryee) -> Self {
self.retries = retries
self.retryee = retryee
return self
}
func retry(retryee: Retryee) -> Self {
return self.retry(1, retryee)
}
func retry(retries: UInt) -> Self {
// For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
self.retries = retries
retryee = nil
return self
}
func retry() -> Self {
return retry(1)
}
func catch(catchee: Catchee) {
var error: NSError?
for numRetries in 0...retries { // First try is retry 0
error = nil
let result = tryee(&error)
if result {
return
} else if numRetries != retries {
if let r = retryee {
tryee = r(error, numRetries)
}
}
}
catchee(error)
}
}
Bool値の代わりにオプションの戻り値をテストするための同様のクラスを作成できます。
class TryOptional<T> {
typealias Tryee = NSErrorPointer -> T?
typealias Catchee = NSError? -> T
typealias Retryee = (NSError?, UInt) -> Tryee
private var tryee: Tryee
private var retries: UInt = 0
private var retryee: Retryee?
init(tryee: Tryee) {
self.tryee = tryee
}
func retry(retries: UInt, retryee: Retryee) -> Self {
self.retries = retries
self.retryee = retryee
return self
}
func retry(retryee: Retryee) -> Self {
return retry(1, retryee)
}
func retry(retries: UInt) -> Self {
// For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
self.retries = retries
retryee = nil
return self
}
func retry() -> Self {
return retry(1)
}
func catch(catchee: Catchee) -> T {
var error: NSError?
for numRetries in 0...retries {
error = nil
let result = tryee(&error)
if let r = result {
return r
} else if numRetries != retries {
if let r = retryee {
tryee = r(error, numRetries)
}
}
}
return catchee(error)
}
}
TryOptionalバージョンでは、オプションではない戻り値の型が適用され、後続のプログラミングが容易になります(例: 'Swift Way:
struct FailableInitializer {
init?(_ id: Int, error: NSErrorPointer) {
// Always fails in example
if error != nil {
error.memory = NSError(domain: "", code: id, userInfo: [:])
}
return nil
}
private init() {
// Empty in example
}
static let fallback = FailableInitializer()
}
func failableInitializer(id: Int)(#error: NSErrorPointer) -> FailableInitializer? { // Curry for retry
return FailableInitializer(id, error: error)
}
var failError: NSError?
var failure1Temp = failableInitializer(1)(error: &failError)
if failure1Temp == nil {
println("failableInitializer failure code: \(failError!.code)")
failure1Temp = FailableInitializer.fallback
}
let failure1 = failure1Temp! // Unwrap
TryOptionalの使用:
let failure2 = TryOptional {
failableInitializer(2)(error: $0)
}.catch {
println("failableInitializer failure code: \($0!.code)")
return FailableInitializer.fallback
}
let failure3 = TryOptional {
failableInitializer(3)(error: $0)
}.retry {
println("failableInitializer failure, on try \($1 + 1), code: \($0!.code)")
return failableInitializer(31)
}.catch {
println("failableInitializer failure code: \($0!.code)")
return FailableInitializer.fallback
}
自動アンラップに注意してください。
編集:この答えは機能しますが、それはObjective-CがSwiftに文字変換されたものに過ぎません。Swift 2.0の変更により廃止されました。上記のGuilherme Torres Castroの回答は、Swiftでのエラー処理の推奨される方法の非常に優れた紹介です。VOS
それを理解するのに少し時間がかかりましたが、私はそれを理解したと思います。それは醜いようですが。Objective-Cバージョンの薄いスキンにすぎません。
NSErrorパラメータを使用して関数を呼び出しています...
var fooError : NSError ? = nil
let someObject = foo(aParam, error:&fooError)
// Check something was returned and look for an error if it wasn't.
if !someObject {
if let error = fooError {
// Handle error
NSLog("This happened: \(error.localizedDescription)")
}
} else {
// Handle success
}`
エラーパラメータを取る関数を記述しています...
func foo(param:ParamObject, error: NSErrorPointer) -> SomeObject {
// Do stuff...
if somethingBadHasHappened {
if error {
error.memory = NSError(domain: domain, code: code, userInfo: [:])
}
return nil
}
// Do more stuff...
}
try catch機能を提供する目的Cの基本的なラッパー。 https://github.com/williamFalcon/SwiftTryCatch
次のように使用します:
SwiftTryCatch.try({ () -> Void in
//try something
}, catch: { (error) -> Void in
//handle error
}, finally: { () -> Void in
//close resources
})
これは、swift 2.0のアップデートの回答です。Javaのような機能豊富なエラー処理モデルを楽しみにしています。最後に、彼らは良いニュースを発表しました。ここに
エラー処理モデル:Swift 2.0の新しいエラー処理モデルは、慣れ親しんだtry、throw、catchキーワードを使用して、即座に自然に感じられます。何よりも、Apple SDKおよびNSErrorと完全に連携するように設計されています。実際、NSErrorはSwiftのErrorTypeに準拠しています。詳細については、Swiftの新機能に関するWWDCセッションをぜひご覧ください。
例:
func loadData() throws { }
func test() {
do {
try loadData()
} catch {
print(error)
}}
ギリェルメトーレスカストロが言ったように、スイフト2.0で、try
、catch
、do
プログラミングに使用することができます。
たとえば、CoreDataでは、データメソッドをフェッチする代わりに &error
にパラメータとしてmanagedContext.executeFetchRequest(fetchRequest, error: &error)
、今、私たちは使用のみを使用する必要がありmanagedContext.executeFetchRequest(fetchRequest)
、その後でエラーを処理しtry
、catch
(Appleのドキュメントリンク)
do {
let fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]
if let results = fetchedResults{
people = results
}
} catch {
print("Could not fetch")
}
xcode7ベータ版を既にダウンロードしている場合。ドキュメントとAPIリファレンスでスローエラーを検索してみてください、最初に表示される結果を選択して。これにより、この新しい構文で実行できることの基本的なアイデアが得られます。ただし、完全なドキュメントはまだ多くのAPIに投稿されていません。
より洗練されたエラー処理技術は、
Swiftの新機能(2015セッション106 28分30秒)
エラー処理はSwift 2.0の新機能です。それは使用していますtry
、throw
とcatch
キーワードを。
例外を処理するための素晴らしくシンプルなlib: TryCatchFinally-Swift
他のいくつかのように、客観的なC例外機能をラップします。
次のように使用します。
try {
println(" try")
}.catch { e in
println(" catch")
}.finally {
println(" finally")
}
Swift 2以降では、他の人がすでに述べたように、エラー処理はdo / try / catchおよびErrorType列挙型を使用することで最適に実行されます。これは同期メソッドの場合は非常にうまく機能しますが、非同期エラー処理には少し賢いことが必要です。
この記事には、この問題に対する優れたアプローチがあります。
https://jeremywsherman.com/blog/2015/06/17/using-swift-throws-with-completion-callbacks/
要約する:
// create a typealias used in completion blocks, for cleaner code
typealias LoadDataResult = () throws -> NSData
// notice the reference to the typealias in the completionHandler
func loadData(someID: String, completionHandler: LoadDataResult -> Void)
{
completionHandler()
}
次に、上記のメソッドの呼び出しは次のようになります。
self.loadData("someString",
completionHandler:
{ result: LoadDataResult in
do
{
let data = try result()
// success - go ahead and work with the data
}
catch
{
// failure - look at the error code and handle accordingly
}
})
これは、非同期関数に個別のerrorHandlerコールバックを渡すよりも少しきれいに見えます。これは、Swift 2以前ではこれがどのように処理されるかでした。
私が見たことは、デバイスの性質上、ユーザーに一連の不可解なエラー処理メッセージを投げたくないということです。そのため、ほとんどの関数がオプションの値を返すので、オプションを無視するようにコーディングするだけです。関数がnilに戻って失敗した場合は、メッセージなどをポップできます。