楽水

人々の創造が自由に表現できる舞台づくり

Swift

Swiftのオプショナル【?や!をわかりやすく解説】

投稿日:2020年8月20日 更新日:


Javaでプログラムを書いたことがあるプログラマーであれば「NullPointerException」というエラーで悩まされた方もいらっしゃるのではないでしょうか。
これは、オブジェクトに対する参照がない、つまり、nullになっている変数にアクセスするときに起こるエラーです。
このNullPointerExceptionを回避するためにはプログラム上で変数がnullになっていないか常にチェックする必要があります。
またJavaだけではなく多くのプログラム言語が変数がnullによるエラーの問題をはらんできます。
このnullアクセスの問題を回避するためにSwiftではオプショナルという仕組みを用意しています。
今回は、このSwiftのオプショナルについて以下の観点で丁寧に解説します。

  • Swiftのオプショナル(?や!)とは何か
  • なぜSwiftにはオプショナル(?や!)が用意されているのか
  • Swiftのオプショナル(?や!)をどのように使うのか

参考本
[改訂新版]Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 WEB+DB PRESS plus

Swiftのオプショナル(?や!)とは何か

まずSwiftの変数や定数はnilを許容しません(Swiftではnullをnilといいます)。
例えば、

var a:String = nil

と書くとnilでは初期化できないというエラーが発生します。
しかし、状況によっては変数がnilになる場合もあり得るのでnilを許容する方法を用意する必要があります。
そこで、Swiftでは型の後に?(クエスチョンマーク)を書いて「値があるかもしれないし無いかもしれない」、つまり、オプショナル(制限を受けない:任意)な値を持たせる仕組みを用意しています。
例えば、

var a:String? = nil

と書くとaは「オプショナルな値を持つ文字列型の変数」となります。
つまり変数aの値はオプショナルでラップされているということです。
例えば、

var a:String? = “a”
print(a)

とするとOptional(“a”)と表示されます。
また、

var a:String?

のように変数を宣言した場合、変数には自動的にnilが設定されます。
それから、以下のようにSwingの Int 型には、String 値を Int 値に変換するイニシャライザがあります。

let possibleNumber = “123”
let convertedNumber = Int(possibleNumber)

しかし、例えば、”Hello”など、あらゆる文字列が整数に変換できるわけではありません。
なので、この場合、convertedNumber は “Int?” 型と推論されます。

このようにSwiftでは、定数または変数が、ある条件において値が存在しない状態を扱う必要がある場合、適切な型のオプショナル値として宣言する必要があります。

なぜSwiftにはオプショナル(?や!)が用意されているのか

先に述べたように多くのプログラム言語が変数がnilによるエラーの問題をはらんできます。
そこで比較的新しい言語であるSwiftはこの問題を回避するために「オプショナル」という仕組みを導入したのです。
さて、例えば、

var a:String?
a.count

のようにオプショナル値にアクセスしようとすると「オプショナル値をアンラップしてください」という内容のエラーが発生します。

このようにSwiftではオプショナル値をアンラップさせてから操作できるようにするという仕組みを通して変数がnilによるエラーの問題を回避しているのです。

Swiftのオプショナル(?や!)をどのように使うか

それでは具体的にオプショナルの使い方について見ていきましょう。
先ほど、
Swiftでは、定数または変数が、ある条件において値が存在しない状態を扱う必要がある場合、適切な型のオプショナル値として宣言する必要がある、
Swiftではオプショナル値をアンラップさせてから操作する
という説明をしました。
ここではオプショナル値をアンラップする以下の3つの方法について紹介します。

  • 強制アンラップ
  • オプショナルバインディング
  • 無条件アンラップ

強制アンラップ

これは、if文で変数がnilでないことを確認した上で強制的にアンラップする方法です。
例えば、以下のようにオプショナルが値を保持していることを確認した後は、オプショナルの名前の末尾に!(エクスクラメーションマーク)を付加して値にアクセスできます。

let possibleNumber = “123”
let convertedNumber = Int(possibleNumber)
//convertedNumber は “Int?” 型です。
if convertedNumber != nil {
print(“convertedNumber has an integer value of \(convertedNumber!).”)
}
// “convertedNumber has an integer value of 123.” と出力

この!は、「このオプショナルが間違いなく値を保持していることがわかっているので、その値を使用する」ということを意味します。
これをオプショナル値の強制アンラップと呼びます。
なお、例えば、

var a:String?
a!.count

のように
値が存在しないオプショナル値を ! でアクセスしようとすると、実行時エラーになります。
なので、! を使ってオプショナル値を強制アンラップする前に、nil でないことを常に確認する必要があります。

オプショナルバインディング

これは、if文やwhile文で変数がnilでないことを確認した上で、その値を一時的な定数として利用できるようにする方法です。
例えば、以下のようにオプショナルが値を保持していることを確認した後は、!を使って強制的にアンラップするのではなく、その値を一時的な定数にバインドして利用できるようにします。

let possibleNumber = “123”
if let actualNumber = Int(possibleNumber) {
print(“\’\(possibleNumber)\’ has an integer value of \(actualNumber)”)
} else {
print(“\’\(possibleNumber)\’ could not be converted to an integer”)
}
// “‘123’ has an integer value of 123” と出力される
//Int(possibleNumber) はオプショナル値を保持します。

この場合、定数がオプショナルが保持している値ですでに初期化されているので、! を付けて(強制アンラップして)値にアクセスする必要はありません。
また、以下のように、1 つの if 文に複数のオプショナルバインディングを含めることができ、ブール条件をチェックするために where 節を使うことができます。

if let firstNumber = Int(“4”), secondNumber = Int(“42”) where firstNumber < secondNumber {
print(“\(firstNumber) < \(secondNumber)”)
}
// “4 < 42” と出力される
//Int(“4”)やInt(“42”) はオプショナル値を保持します。

無条件アンラップ

オプショナルは、定数や変数に値が存在しない場合があることを示していますが、値が設定された後など、オプショナルが常に値が持っていることがプログラムの構成から明らかな場合もあります。
こういったケースでは、常に値があることを前提にしてアクセスのたびにオプショナル値をチェックしてアンラップする処理をするのは効率がよくありません。
このような場合、以下のように、オプショナルにしたい型の直後にクエスチョンマーク (String?) を置くのではなく、エクスクラメーションマーク (String!) を置いて無条件にアンラップされるオプショナル(使用時に自動的にアンラップされることが許可されているオプショナル)を記述します。

var a:String!

この場合、a:String?のときと同様、変数には自動的にnilが設定されます。
以下の例の場合、通常のオプショナルなのでアクセスのたびにオプショナル値を!で強制的にアンラップする必要があります。

let possibleString: String? = “An optional string.”
let forcedString: String = possibleString! // エクスクラメーションマークが必要

しかし、!を使って無条件アンラップされるオプショナルにした場合、アンラップする必要なくアクセスすることができ手間が省けます。

let assumedString: String! = “An implicitly unwrapped optional string.”
let implicitString: String = assumedString // エクスクラメーションマークは不要

なお、以下のように無条件にアンラップされるオプショナルが nil のときラップされている値にアクセスしようとすると、実行時エラーになります。

var a:String!
a.count

これは、以下のように値が存在しない通常のオプショナルに、エクスクラメーションマークを付けた場合の実行時エラーとまったく同じ結果です。

var a:String?
a!.count

以上より、変数がどこかで nil になり得る場合は、無条件にアンラップされるオプショナルを使用せずに通常のオプショナルを使用するようにします。
以上、今回は、Swiftのオプショナルについて解説しました。

-Swift
-, ,

執筆者:


  1. […] ショナルな HTMLElement として定義されています。 オプショナルについては、 Swiftのオプショナル を参照してください。 上記の例の場合、以下のようにクラスインスタンスとクロ […]

関連記事

Swiftのクラスと構造体をわかりやすく解説

Swiftを学ぶ過程で、クラスと構造体は何が違うのか知りたいと考える人もいると思います。 そこで、今回は、以下の観点でSwiftのクラスと構造体について解説します。 クラスと構造体の定義 クラスと構造 …

Swiftの継承についてわかりやすく解説

クラスは、別のクラスからメソッド、プロパティ、および他の特徴を継承することができます。 あるクラスが別のクラスから継承するとき、継承するクラスをサブクラス、継承されるクラスをスーパークラスと呼びます。 …

Swiftの列挙型をわかりやすく解説

プログラムを作成するとき、曜日(月曜日、火曜日、水曜日、木曜日、金曜日、土曜日、日曜日)やカードのスート(スペード、クラブ、ダイヤ、ハード)のように決まった範囲の要素(種類)を持つ集合を扱う場合がある …

Swiftの関数のネストについてわかりやすく解説

Swiftでは、関数のスコープ内に便利な機能を包むために、他の関数の中に関数を記述することができます。 ネストされた関数を別のスコープで使用できるように、ネストしている関数からネストされた関数を返すこ …

Swiftのクロージャについてわかりやすく解説

Swiftのクロージャって何? いまひとつわからない、という方むけに、今回は、Swiftのクロージャについて以下の観点で丁寧に解説します。 Swiftのクロージャとは何か Swiftのクロージャ式 S …