1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

SwiftAdvent Calendar 2018

Day 4

SwiftのOptionalを理解する

Posted at

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

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

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

Lispは実装方法によるが内部ではnilを値と要素の二通りがあるようだ。

それと比較して、C言語は質実剛健。簡素で実用的だ。

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

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

開示(unwrap)

Optional変数に!をつけると、Optionalでない変数に変えられる。Optional変数がnilだったら実行時にエラーとなる。

C言語のポインターに近い挙動ということか。

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目的で

オプショナル束縛構文 optional binding

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

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
}

guard文

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

guard 条件 else { /* breakやreturn */ }
func demo(_ num:Int?) {
    guard let n = num else { return }
    print(“\(n)”)  // 変数nが使える
}

nil合体演算子

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

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の値

inout引数

Swiftの関数は、C言語と同様に値渡しだが、C++の参照渡しに相当するのがinout引数。

ただ、実引数に&をつけることから、C言語のポインターの値渡しをポインターであることを隠蔽した構文ということかなと思う。

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”)

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

有値オプショナル型 (IUO)

有値オプショナル型 (implicitly unwapped optional) は、オプショナル型だが、値が格納されていることが分かっている場合のための構文。

おそらく、Objective-C / Cocoa との互換性のためのもので、例えば、InterfaceBuilderのOutletなどで利用されいるようだ。

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

失敗のあるイニシャライザ

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

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)

キャスト演算子

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

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

オプショナルチェーン optional chaining

オプショナル束縛構文は、続けて記述できる。

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

【関連情報】
Cocoa Advent Calendar 2018

Cocoa.swift 2019-01

Cocoa.swift

Cocoa勉強会 関東

Cocoa練習帳

Qiita

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?