SwiftのOptionalを理解する

<p>iOSアプリケーション開発を主な業務としているが、チームの都合でObjective-Cを選択している。そんなSwiftに不慣れな自分にとって厄介なのはOptional。色んな場面で様々な形式で出てくるため混乱する。そこで自分自身がOptionalを習得するため、自分が見つけられたOptional関連のコードを飼料化してみた。</p>

<p></p>

# 無
<p>プログラミングが生まれた頃から、様々な方法で無を表現することが試みられている。</p>

<ul>
<li>Lispでは、無を表すものとしてnilを用意。</li>
<li>C言語では、空ポインタとしてNULLマクロを定義。</li>
<li>Objective-Cでは、空idとしてnilが用意され、画期的なのはnilにメッセージを送信しても無視されるだけでエラーにならない！</li>
<li>SwiftのnilはObjective-Cとの互換。Cocoaフレームワークを利用するためか。</li>
</ul>

<p>Lispは実装方法によるが内部ではnilを値と要素の二通りがあるようだ。</p>
<p>それと比較して、C言語は質実剛健。簡素で実用的だ。</p>

<pre><code>var a : Int = 1
var b : Int? = 2
a = nil  // エラー
b = nil  // OK
b = Int(“abcd”)  // nil
var c : Optional<Int> = 3  // パラメータ付き型指定
</code></pre>

<p>SwiftでOptionalといえばInt?と型に?がついた宣言ということになるが、厳密にはパラメータ付き型指定の糖衣構文とうことになる。Optional変数にはnilを代入することが出来るが、Optional出ない型とは異なる型ということになる。</p>

# 開示（unwrap）
<p>Optional変数に!をつけると、Optionalでない変数に変えられる。Optional変数がnilだったら実行時にエラーとなる。</p>
<p>C言語のポインターに近い挙動ということか。</p>

<pre><code>var a : Int? = 1234
var b : Int = a - 2  // 型が異なるのでコンパイル・エラー
var b : Int = a! - 2  // 開示指定する
a = nil
b = a! - 2  // 実行時エラー
 
a = 5678
if a != nil {
    print(“\(a!)”)  // 開示指定が必要
}
print(String(describing: a))  // Debug目的で
</code></pre>

# オプショナル束縛構文 optional binding
<p>C言語のNULLチェックをして利用するというパターン化されたコードをスマートにしたのが、オプショナル束縛構文 か？</p>

<pre><code>var num : Int? = 1234
if let n = num {
    print(“\(n)”)
}
 
if var n = Int(“1234”) {
    n += 5678
    print(“\(n)”)
}
 
if let n = Int(“1234”), let m = Int(“5678”) {
    print(“\(n + m)”)
}
 
var a : Int? = 1
while let n = a {
    a = nil
}
</code></pre>

# guard文
<p>if分によってインデントが深くなることを避けるため、例えば、関数の先頭でNULLチェックをして、NULLだったら直ぐにreturnするというパターン化されたコードがあるが、これのために用意されたのが、guard文。オプショナル束縛構文の糖衣構文ということのようだ。</p>

<pre><code>guard 条件 else { /* breakやreturn */ }
</code></pre>

<pre><code>func demo(_ num:Int?) {
    guard let n = num else { return }
    print(“\(n)”)  // 変数nが使える
}
</code></pre>

# nil合体演算子
<p>三項演算子で値がnilなら指定した値を、nilでない場合はその値を返すというパターン化されたコードが必要になると思うが、これについても糖衣構文が用意されている。</p>

<pre><code>let n : Int? = 1234
 
let m = (n != nil) ? n! : 0
let m = n ?? 0
 
let a : Int? = nil
let b : Int? = nil
let c : Int? = 3
let = a ?? b ?? c ?? 0  // cの値
</code></pre>

# inout引数
<p>Swiftの関数は、C言語と同様に値渡しだが、C++の参照渡しに相当するのがinout引数。</p>
<p>ただ、実引数に&をつけることから、C言語のポインターの値渡しをポインターであることを隠蔽した構文ということかなと思う。</p>

<pre><code>func demo(_ p: inout Int?) {
    p = nil
}
var n: Int? = 1234
demo(&n)
print(n ?? “nil”)
 
func test(_ num: inout Int) {
    num = 0
}
n = 5678
test(&n!)  // nがnilだと実行時エラー
print(n ?? “nil”)
</code></pre>

<p>実引数が計算型プロパティだった場合は、関数内での変更はコピーに対して行われる。</p>

# 有値オプショナル型 (IUO)
<p>有値オプショナル型 (implicitly unwapped optional) は、オプショナル型だが、値が格納されていることが分かっている場合のための構文。</p>
<p>おそらく、Objective-C / Cocoa との互換性のためのもので、例えば、InterfaceBuilderのOutletなどで利用されいるようだ。</p>

<pre><code>let n : Int! = 1234
print(“\(n)”)  // 開示指定は不要
 
var m : Int! = nil
m += 5678  // 実行時エラー
print(“\(m)”)
</code></pre>

# 失敗のあるイニシャライザ
<p>自分の調査が足りなかったら申し訳ないで、Swiftが登場した当初、Optional型とはNSObjectを継承したクラスだったと思うが、言語的には曖昧だと思う。このOptional型の定義を厳密にするために用意されたのが、失敗のあるイニシャライザ ということか？</p>

<pre><code>struct Demo {
    var a = 0
    init?(_ n:Int) {
        if n < 0 {
            return nil
        }
        a = n
    }
    init() {
        a = 1234
    }
}
var p: Demo = Demo()
var q: Demo? = Demo(5678)
</code></pre>

# キャスト演算子
<p>Swiftの言語仕様書のOptionalの章に含まれるものではないようだが、Optionalの話で大事な構文がキャスト演算子だ。列挙してみる。</p>

<dl>
<dt>式 is T</dt> <dd>型/プロトコルTなら真</dd>
<dt>式  as T</dt> <dd>型/プロトコルTにキャスト</dd>
<dt>式 as? T</dt> <dd>型/プロトコルTのオプショナルにキャスト<br />失敗した場合はnil</dd>
<dt>式 as! T</dt> <dd>型/プロトコルTにキャスト<br />失敗した場合は実行時エラー</dd>
</dl>

# オプショナルチェーン optional chaining
<p>オプショナル束縛構文は、続けて記述できる。</p>

<pre><code>// 辿っている途中でnilがあれば、
// そこで止まり全体でnilとなる。
if let name = who?.club?.teacher?.name {
    print(name)
}
</code></pre>

【関連情報】
<a href="https://qiita.com/advent-calendar/2018/cocoa">Cocoa Advent Calendar 2018</a><br />
<a href="https://cocoa-kanto.connpass.com/event/111294/">Cocoa.swift 2019-01</a><br />
<a href="https://cocoa-kanto.connpass.com/">Cocoa.swift</a><br />
<a href="https://www.facebook.com/groups/cocoa.kanto/">Cocoa勉強会 関東</a><br />
<a href="http://www.bitz.co.jp/weblog/">Cocoa練習帳</a><br />
<a href="https://qiita.com/m_yukio">Qiita</a><br />