【Xcode(Swift)】Optional型をマスターする!!

Xcode(Swift/Objective-C)における、技術メモです。
自身の脳内整理・備忘録を兼ねてメモしています。

今回はSwift言語の特徴である「Optional型」。
自身も、時間が経ってからSwift言語を触ると「えーと、、、」となってしまいます。
プログラミング初級者もハマると思われる「Optional型」。
ざっくりと解説します。(一部、省略しています。)

※もし内容に誤りなどございましたら、下部「コメント」欄にいただけるとありがたいです。

概念

プログラミングの大敵、「nil」。(プログラミング言語によっては、「null」とか「none」とか言います。)
「値がない」という意味ですが、変数にセットされている値が「nil」であるにも関わらず変数を使用した場合、処理ができず、プログラムが落ちます。
もちろん適切なエラーハンドリングをすればよいのですが、実際問題、全てにおいて完璧にハンドリングするには至難の業です。

そこでSwift言語として用意された仕組みが「Optional型」です。

「Optional型」のみ「nil」の状態が許容できる形とすることで、コンパイルレベルでnil問題を検知することができます。
(「非Optional型」は「nil」を許容しません。)

煩雑ではありますが、この仕掛けがあることで想定外の「nil」によりプログラムが落ちることもなく、安定したアプリケーションを作りやすくなります。
(プログラマーの「考慮漏れ」「うっかり」を防ぐための仕組みですよ!)

「Optional型」は、「それぞれの型」をラッピングできるもの、というイメージになります。
例えば数値型 Int には 「Optional<Int>」と「Int」の2つの状態が存在することになります。(後者は「nil」を許容しない)

最初はなかなかしっくりとこないと思いますが、Xcodeでもなんだかんだケチ(エラーが出る)つけられるので、たくさん書いて理解していきましょう!

仕様と使用例

Optional型

  • 「Optional型」は「nil」が許容できます。
  • 「Optional型」の「変数の値を使う場合(加算する、など)」は、変数を「アンラップ」する必要があります。
  • 「Optional型」に値をセットすることはできます。(ラップされた形で格納されます)
  • 「?」をつけて宣言します。
  • 「Optional型」を使う時は、基本的には「オプショナルバインディング」「??演算子」を利用したコーディングをしましょう。(極力、強制アンラップを避ける)
// Optional型の変数の作り方(どちらの書き方でも同じ意味です)
var count: Optional<Int>
var count: Int?                        // 「?」をつけるとOptional型として宣言したことになる

// Optional型に値をセット
count = 1 + 1         
print(count)             // Optional(2)

// Optional型の「値を使う」場合はコンパイルエラーとなります。
// 「値を使う」という意味には、クラスメソッドを使う、なども含まれます。(使うにはアンラップが必要)
// これは値が「nil」の場合が可能性としてあり、
// その場合はプログラムが処理できないため、「Optional型」のままでは使わせないという仕様になります。
// 強制アンラップ(!)することで「値を使う」ことができますが、この時値が「nil」だと、当然プログラムが落ちます。
let addCount:Int?
addCount = 3                   // Optional(3)
count = 1 + addCount     // addCountが「Optional型」であるため、コンパイルエラーとなる(=コンパイル時に検知できる)
count = 1 + addCount!    // addCountを強制アンラップ(!)すれば利用可能。countの中身は Optional(4) となる。

使い方:オプショナルバインディング(Optional Binding)

  • 値が「nil」 の場合も考慮した、安全な取り扱いができる方法。
  • 「nil」の可能性があり「nil」の場合は何も処理する必要がないケースの場合は、基本的にこの構文を使いましょう。
    (例えば、画面のとあるLabelの値を取得して、値がある場合は何かの処理をする、といった場合。)
  • if-let 文を使います。
  • 「nil」の場合は処理を行わずに後続(ifの外)に進むことができます。
// etcStringに値が入っている場合のみ、ifの中の処理を実行
if let testString = etcString {
    // 値が存在した場合に実行したい処理を記述
    print(testString)
}

使い方:オプショナルチェインニング(Optional Chaining)

  • メソッドやプロパティ等に「?」をつけて使う方法。
  • いちいちアンラップせずともメソッドやプロパティを使うことができます。
  • 使おうとした値が「nil」の場合でも実行時エラーとならず、「Optional(nil)」が返ってくる。(のでプログラムが落ちない)
  • 「nil」の場合を含め、「Optional型」で返ってくる。
  • プロパティやメソッドをつないで記述する時に便利。(途中で「nil」となった場合、後続の処理は中止する。)
    =つなげて記述することから、「Chaining」と呼んでいると思われます。
// cellForRow()、textLabel のどの地点で「nil」となってもプログラムが落ちず、
// その場合でも「Optional(nil)」が返ってきて処理が続行できる
let labelName:String? = tableView.cellForRow(at: indexPath)?.textLabel?.text

使い方:デフォルト値を指定(??演算子)

  • 値が「nil」の場合、代わりに使うデフォルト値を設定する方法。
  • 「??演算子」を使用します。
  • 「nil」の場合は特定の値として処理をさせたいケースに利用。
  • 該当コード箇所で想定した処理が組めるため、安全なプログラムを書きやすい。
// 例:addCountの値を+1 して totalCountにセットする
let addCount: Int?
addCount = Int(textLabel?.text)            // textLabelに設定されている値を取得、なければOptional(nil)がセットされる 
let totalCount = (addCount ?? 0) + 1    // addCountに値があればその値+1、Optional(nil)であれば、0として計算する

使い方:強制アンラップ

  • 強制的にアンラップして値を取り出してしまう方法。
  • 「!」を付与します。
  • 「nil」を強制アンラップするとプログラムが落ちます。
  • 絶対に「nil」が入っていないと確信できるケース、値がない場合は終了させたいケース以外は利用をなるべく避けましょう。
    (コンパイル時に検出する、というSwift言語の特徴を捨てることになります。バグ発見の先送りでもあります。)
// 例:count を強制アンラップ
let totalCount = count! + 1    // countが「nil」の場合、プログラムが落ちます

使い方:アンラップ不要の形での宣言(暗黙的アンラップ)

  • 値にアクセスする特に自動的に強制アンラップしてくれる方法。
  • 宣言時に「!」を使用。
  • コードはシンプルになるが、強制アンラップと同様、危険な側面があるため、乱用すべきではない。
var totalCount = 5
let count: Int! = 1                  // 暗黙的アンラップで宣言し、1をセット
totalCount = count + 1         // count! としなくとも、アンラップされて計算ができる

関連リンク

Qiita

はじめに 以前の記事は一部内容が古くなったので、全面的に書き直しました 環境 Xcode 6.1 G…

記事情報や役立ち情報を発信します!
Click to Hide Advanced Floating Content
Click to Hide Advanced Floating Content