メモ
optional
Swift

[Swift] 「!」、「?」マーク(Optional)についてのメモ

More than 3 years have passed since last update.

※ あくまでメモなので、多分に推測が含まれています。


さっそくSwiftを触ってみています。
ざっと見た感じだと、JavaScriptとRubyみたいな感じだなーと思いました。
(きっと触ってきた言語などによって感想は様々でしょうが)

ただ、Appleの発表にあったように「モダンな」感じは伝わってきます。

しばらくはiBooksの教科書を読んだりして理解できてきた部分をメモとして書いておこうと思います。
言語の話なので、コードを抜粋しなければとりあえず規約的には問題ないかな、と。

さて、さっそく読んでいて躓いたというか、迷ったものに、タイトルの通り!?があります。
こんなコードです。

sample.swift
var a: String? = "hoge"
var b: String! = "hoge"

最初見た時、この?!はなんだ? と。

Optionalな型

結論から言うと、どうやらこれはOptionalというもののようです。
ちなみにこの?は、宣言時と使用時で意味が若干変わってくるようです。
例えば、

sample.swift
var a: String? = "hoge"

は、Optionalな型を宣言しています。
一方、利用する場合は

sample.swift
// なにがしかのクラスのインスタンスを作る
var b: AnyClass = AnyClass()

b.hoge?.foo()

となります。

Optionalは不確定要素

この場合の?はまさにOptionalな変数を利用する意味で、hogeという変数はあくまでオプション、ということになります。
つまりオプションなので「存在していないかも」しれません。
なので、?を付けて、「存在していたら」という形でアクセスしている、というわけです。

[追記]
※ ちなみにSwiftでは、nil対してメソッドを呼び出した場合はnilが返るようです。
そのため、上記のb.hoge?.foo()が正常に動作する、というわけです。

「はてな」という記号は、まさにそのままの意味で使われていますね。
bhogeを持っている? と問い合わせていると考えると分かりやすいと思います。

Optionalは重い?

これは、会社の同僚と話していて出てきた答えですが、Optionalは宣言された型「か」nilを保持しています。
オプションですからね。(そして通常のInt型などは普通に宣言するとnilを持つことが出来ません)

つまり、宣言された変数は2つの状態(型かnilか)があり、参照する際は都度、問い合わせて中身を確認しなければならない、ということです。
これは、中身が確定している場合には余計な処理です。

ちなみに、他の言語でも同様の仕組みとして「オプション」というものがあるようです。そして、オプションは2つの要素を持てるように曖昧な形に「wrap」されている、と表現するようです。

OptionalをUnwrapするImplicitlyUnwrappedOptional

上記で書いたように、Optionalは「wrap」されている状態です。
そのために、参照の際に都度確認する必要があったわけです。

が、中身が(プログラマが自信を持って)確定している、と判断できる場合は、「wrap」状態を「unwrap」してから使うほうが効率的です。
そのために用意されているのがImplicitlyUnwrappedOptionalです。

そしてこれは、構文的には!が使われます。
つまり、「 ?で曖昧に宣言したものを!で確信に変える 」と考えると分かりやすいです。
(ゲームとかでなにか発見すると頭に「!」が出たりしますよね、あれですw)

未確定 と 確定

というのが、理解した内容です。
実際の使用例を見ると分かりやすいかもしれません。

sample.swift
class Hoge {
    var age: Int?
    var fuga: Fuga?
    init(name: String, age: Int) {
        self.age = age
        self.fuga = Fuga(name: name)
    }
}

class Fuga {
    var name: String?
    init(name: String) {
        self.name = name
    }
    func say() {
        println("My name is \(self.name)")
    }
}

var hoge = Hoge(name: "namae", age: 16)

// fugaに直接アクセスすることはできない
// hoge.fuga.say() => コンパイルエラー

// fugaはオプションなので、存在しているか確認する必要がある
hoge.fuga?.say()

// fugaを明示的にUnwrapしてアクセスもできるが、`nil`が入っていた場合はランタイムエラー
// つまり、Unwrapする必要があるものを、存在が確信できるものに利用する
hoge.fuga!.say()

色々ドキュメントを読んでいると、?はまさにオプションという認識で大丈夫そうですが、!に関してはunownedと表現されています。(?weak

個人的な解釈としては、!nilの可能性があるが、参照する際はプログラマが確実にnilではない状態と思えるもの、と思っています。
(例えばなにかのクラスのインスタンスを保持しているクラスがあり、そのクラスからインスタンスを利用する場合、みたいな感じでしょうか。生成されるまではnilの状態がありえるが、生成してからしか利用しない、みたいな)

型宣言は格納できる状態が異なる

Optionalに関しては、上記のイメージで使用できると思います。
ただ、宣言時と代入時にも若干の制約があります。
これは、宣言した型が持てる状態と、代入しようとしている状態をイメージするといいと思います。
つまりコードにすると以下のようになります。

sample.swift
var anyNum: Int = 5

// `Int`型か`nil`を受け取れるため、以下は問題なし
var a: Int? = anyNum
var b: Int? = nil

// `Int`型は`nil`を保持できない
var c: Int = nil


var anyNum2: Int?

// `Optional`な型を直接`Int`型には入れられない
// `nil`の「可能性」があるため
var d: Int = anyNum2

// Unwrapすることで`Int`型と確定するので代入できる
// ただし、`nil`の場合がありえるので、その場合はランタイムエラーになる(コンパイルはできる)
var e: Int = anyNum2!

// `Optional`だが、明示的に`!`で変換される?(ちょっとここは曖昧)
var f: Int! = anyNum2

// `5`が`Int`型なのは明らか
var g: Int! = 5

// `anyNum2`を明示的にUnwrapしてから代入。これも明らかに`Int`。(実行時どうかは置いておいて)
var h: Int! = anyNum2!

Optional<T>ImplicitlyUnwrappedOptional<T>

(おそらく)!?は上記のシンタックスシュガー的なものかなと。(ただ、微妙に挙動は異なる)
上記をそれに置き換えると以下のようになる。

sample.swift
var anyNum: Int = 5
var a: Optional<Int> = anyNum
var b: Optional<Int> = nil

var anyNum2: Int? = 10
var g: ImplicitlyUnwrappedOptional<Int> = 5
var h: ImplicitlyUnwrappedOptional<Int> = anyNum2!
var i: ImplicitlyUnwrappedOptional<Int> = nil