【Swift入門】do-catchとtryでエラー処理を記述する方法

こんにちは!フリーエンジニアのせきです。

プログラムを実行すると様々なエラーが発生し、それに対する処理を行う必要があります。

この記事では、

・エラー処理とは何か知りたい
・エラーを発生させる方法を知りたい
・エラーを処理する方法を知りたい

という基本的な内容から、

・「try?」「try!」の用途を知りたい
・エラーが発生しても行う処理を記述する方法を知りたい

といった応用的な内容に関しても解説していきます。

今回はそんなエラー処理を記述する方法について、わかりやすく解説します!

目次

エラー処理とは

プログラムを実行すると、予期しないエラーが発生する場合があります。

そのエラーに対して適切な処理をしていないと、プログラムはそこで異常終了してしまいます。

今回はエラーに対する処理の書き方を解説していきます。

ErrorとNSError

Swiftでは実行時に発生するエラーを、Errorプロトコルで扱います。

プロトコルとはJavaやC#でいうインタフェースで、クラスや構造体が実装するプロパティとメソッドを定義してあるものです。

自分でErrorプロトコルを適用した列挙型や構造体を使用することもできますが、今回はApple標準フレームワークのFoundationに含まれているNSErrorを使用していきます。

Swift2.0まではErrorTypeプロトコルを使用していましたが、Swift3.0からErrorプロトコルに変更されたので、注意してください。

エラーを発生させる

throwでエラーを投げる

エラーを発生させ、そのエラーの処理を呼び出し元に任せることを「エラーを投げる」といいます。

エラーを投げるには、throw文を使用します。

書き方:

throw NSError(domain: String, code: Int, userInfo: [String : Any]? = nil)

NSErrorは、エラーが発生した箇所を特定するためのドメイン、エラーコード、任意の情報を指定してエラーを投げることができます。

throwsでメソッドを定義する

エラーを投げる可能性のあるメソッドは、メソッドの定義にthrowsキーワードを書きます。

書き方:

func メソッド名(引数) throws -> 戻り値 {
	// エラーを投げる可能性のある処理
}

以下は、引数に空文字列が指定されたらエラーを投げるメソッドのサンプルです。

サンプルプログラム:

import Foundation

func printText(text: String) throws {
	if text.isEmpty {
		throw NSError(domain: "error", code: -1, userInfo: nil)
	}
	print(text)
}

「text.isEmpty」は、textが空文字の場合にtrueを返します。

textが空文字の場合、「throw NSError(…)」でエラーを投げます

そのため、メソッドの定義にはthrowsキーワードが必要になります。

エラーを対処する

上の章のようにエラーを投げる可能性のあるメソッドを呼び出す側は、エラーを対処する必要があります。

do-catchとtryでエラーを受け取る

投げられたエラーを受け取り対処するには、do-catch構文を使用します。

書き方:

do {
	try メソッド呼び出し
} catch {
	// エラーが発生した場合の処理
}

do節でエラーを投げる可能性のあるメソッドを呼び出します。

呼び出し時には、tryキーワードを書きます。

エラーが発生した場合の処理はcatch節に書きます。

以下は、上の章で作成したエラーを投げるメソッドを呼び出すサンプルです。

サンプルプログラム:

import Foundation

func printText(text: String) throws {
	if text.isEmpty {
		throw NSError(domain: "error", code: -1, userInfo: nil)
	}
	print(text)
}

do {
	try printText(text: "");
} catch {
	print("エラーが発生しました")
}

実行結果:

エラーが発生しました

printTextメソッドはthrowsキーワードでエラーが発生する宣言がされているので、「try printText」のようにtryキーワードを使用します。

サンプルでは引数に空文字を指定しているので、エラーが発生し、catch節の処理が実行されています。

tryキーワードがなかったり、do-catch構文で囲われていないとコンパイルエラーになりますので注意してください。

try?とtry!でエラーを無視する

エラーを投げる可能性のあるメソッドでも、エラーを無視して呼び出すこともできます。

try?とは

tryキーワードに「?」をつけることで、エラーを無視することができます。

サンプルプログラム:

import Foundation

func printText(text: String) throws {
	if text.isEmpty {
		throw NSError(domain: "error", code: -1, userInfo: nil)
	}
	print(text)
}

try? printText(text: "");

ここでは、printTextメソッドの呼び出しをdo-catch構文で囲っていませんが、コンパイルエラーになりません

実行するとエラーは発生しますが無視され、何も表示されません。

try!とは

tryキーワードに「!」をつけても、エラーを無視することができます。

ただし、「try!」はエラーが発生した場合に実行時エラー(クラッシュ)になります。

そのため、エラーが起こり得ないケースで使用するのに限られます。

エラーが発生しても行う処理

deferで処理を定義する

エラーが発生するとそこで処理が中断し、呼び出し元に戻ってしまいます。

処理が中断しても行いたい処理がある場合は、do-catch構文の中でdefer文を使用します。

用途はJavaやC#のfinally文と同じですが、tryでメソッドを呼び出す前に定義する点が異なります。

書き方:

do {
	defer { 処理1 }
	defer { 処理2 }
	try メソッド呼び出し
	defer { 処理3 }
} catch {
	// エラーが発生した場合の処理
}

メソッド呼び出しでエラーが発生しなかった場合はメソッド処理後、「処理3」→「処理2」→「処理1」の順で実行されます。

メソッド呼び出しでエラーが発生した場合はメソッド処理後、「処理3」は実行されず、「処理2」→「処理1」の順で実行されます。

サンプルです。

サンプルプログラム:

import Foundation

func printText(text: String) throws {
	if text.isEmpty {
		throw NSError(domain: "error", code: -1, userInfo: nil)
	}
	print(text)
}

do {
	defer {print("defer1")}
	defer {print("defer2")}
	try printText(text: "");
	defer {print("defer3")}
} catch {
	print("エラーが発生しました")
}

実行結果:

defer2
defer1
エラーが発生しました

メソッド呼び出しでエラーが発生しているので、その後に記述しているdefer文は実行されていません。

まとめ

今回はエラー処理を記述する方法について解説しました。

実際のシステム構築ではエラーが発生した時に異常終了を避けるため、エラー処理はかかせません。

エラー処理を記述する方法を忘れてしまったら、この記事を思い出して下さい!

この記事を書いた人

フリーランスでWebシステム開発やゲーム開発をしています。
読者の方にプログラミングの面白さをお伝えしたいです。

目次