tetu式

ゲームと音楽・作曲の自己満足と悩みどころの多いプログラムのブログ。

Xcode SwiftにおけるOptional型とは。その利点は?

今回は前回の記事でちらっと話題にした
Optional型の話。

今までプログラムやってて聞いたことの無い型なのでちょっと念入りに。



そもそもOptional型とは?

数値型や文字列型の一種というものではなく、
通常の数値型や文字列型に空の状態を保持できる変数型になります。


なんのこっちゃ?
空の状態というのは変数を定義して初期値を入れないという意味?

いいえ、違います。

空の状態を保持、というのは
変数に nil という値を入れても大丈夫ということです。
近い意味で言えば null を変数に入れる値として許可するかしないかといったところ。

通常SwiftではOptional型以外の変数は nil を許可していません。

例えば・・・

var numA: Int;

print(numA);

こういう書き方をすると

variable 'numA' used before being initialized

というように怒られます。

numAを初期化する前に使おうとした って感じですね。

var numA: Int = nil;

print(numA);

じゃぁ nil は入るかと言ったら、許可されてないので入りません。
この場合は
nil cannot initialize specified type 'Int'
というエラーが表示。

Int型は nil で初期化できません って感じ。

じゃぁ nil を許可するInt型にしよう
var numA: Int? = nil;

print(numA);    // nil

このように型名の後ろに ? を入れることでOptional型のInt型になります。
ちなみに初期値無しの場合は勝手に nil が入ります。
これでprintで nil と表示されるようになります。
もちろん数値を入れればその数値が変数に入りますが・・・

var numA: Int?;

numA = 3;

print(numA);    // Optional(3)

printではOptional(3)という風に表示されます。
これを通常の数値として扱うにはアンラップする必要があります。

var numA: Int?;

numA = 3;

print(numA);    // Optional(3)
print(numA!);    // 3

ただの数値(Int型)としてアンラップするにはOptional型の後ろに ! をつけます。


イメージとしてはInt型をOptional型で包み、
Int型を使うためにはOptional型から取り出さなければ使えないという感じ?

通常Int型は何も入っていない場合、見ることができません。
これは本記事最初のソースで書いた内容ですね。

しかし、Optional型にすることで何も入ってないInt型を nil という物として
見ることができます。

これの利点として、いわゆるnilチェックを簡単にすることができます。

var numA: Int?;

// numA = 3;

if let numW = numA {
    print(numW);
} else {
    print("値が未設定");
}

例えばこんなソースがあったとします。
if文で代入する書き方っていうのもあまり見ないと思いますが・・・
Swiftではこういうif文はOptional型の numA が空(nil)であればfalse、
値が何かあればtrueという判定をします。
と同時に、trueであればアンラップした値を代入してくれます。
こういう書き方をオプショナルバインディングと言うそうです。


ちなみに先の ! を使ってOptional型から中身を取り出す方法を
強制アンラップって言います。
これを使う場合、nilのままのOptional型に対して行うとエラーになるので注意。
こういうエラーを避けるのに使うのがオプショナルバインディングってわけですね。


上のソースの場合、numAに3を代入する部分はコメント化されているので
numAは nil になっています。
その状態でif文に入るとfalseになり、"値が未設定"がコンソールに出力。


コメント部分を解除すると numA には Optional(3) が入ります。
こちらはif文の判定がtrueになり、アンラップした値がnumWに入るので
3 がコンソールに出力されます。


こんな風にnilチェックと代入を一度のif文で行ってくれるため、ソースの意味を
理解すると非常に見やすい文になります。


ちなみにnumA = 0;とした場合でもtrueになるため、0 が出力されます。
if文で 0 が入っているのにtrueになるというのがちょっと違和感ありますが
値が入ってるのに0だからfalseっていう
他の言語でありがちなミスを防ぐこともできます。


このようなチェックが必要になることは安全なプログラムを書く上で
必要なことなので、この文を書く手間が簡単になるとありがたいですね。