※ あくまでメモなので、多分に推測が含まれています。
さっそくSwiftを触ってみています。
ざっと見た感じだと、JavaScriptとRubyみたいな感じだなーと思いました。
(きっと触ってきた言語などによって感想は様々でしょうが)
ただ、Appleの発表にあったように「モダンな」感じは伝わってきます。
しばらくはiBooksの教科書を読んだりして理解できてきた部分をメモとして書いておこうと思います。
言語の話なので、コードを抜粋しなければとりあえず規約的には問題ないかな、と。
さて、さっそく読んでいて躓いたというか、迷ったものに、タイトルの通り!
と?
があります。
こんなコードです。
var a: String? = "hoge"
var b: String! = "hoge"
最初見た時、この?
や!
はなんだ? と。
Optionalな型
結論から言うと、どうやらこれはOptional
というもののようです。
ちなみにこの?
は、宣言時と使用時で意味が若干変わってくるようです。
例えば、
var a: String? = "hoge"
は、Optional
な型を宣言しています。
一方、利用する場合は
// なにがしかのクラスのインスタンスを作る
var b: AnyClass = AnyClass()
b.hoge?.foo()
となります。
Optionalは不確定要素
この場合の?
はまさにOptional
な変数を利用する意味で、hoge
という変数はあくまでオプション、ということになります。
つまりオプションなので「存在していないかも」しれません。
なので、?
を付けて、「存在していたら」という形でアクセスしている、というわけです。
[追記]
※ ちなみにSwiftでは、nil
対してメソッドを呼び出した場合はnil
が返るようです。
そのため、上記のb.hoge?.foo()
が正常に動作する、というわけです。
「はてな」という記号は、まさにそのままの意味で使われていますね。
b
はhoge
を持っている? と問い合わせていると考えると分かりやすいと思います。
Optionalは重い?
これは、会社の同僚と話していて出てきた答えですが、Optionalは宣言された型「か」nil
を保持しています。
オプションですからね。(そして通常のInt
型などは普通に宣言するとnil
を持つことが出来ません)
つまり、宣言された変数は2つの状態(型かnil
か)があり、参照する際は都度、問い合わせて中身を確認しなければならない、ということです。
これは、中身が確定している場合には余計な処理です。
ちなみに、他の言語でも同様の仕組みとして「オプション」というものがあるようです。そして、オプションは2つの要素を持てるように曖昧な形に「wrap」されている、と表現するようです。
Optional
をUnwrapするImplicitlyUnwrappedOptional
上記で書いたように、Optional
は「wrap」されている状態です。
そのために、参照の際に都度確認する必要があったわけです。
が、中身が(プログラマが自信を持って)確定している、と判断できる場合は、「wrap」状態を「unwrap」してから使うほうが効率的です。
そのために用意されているのがImplicitlyUnwrappedOptional
です。
そしてこれは、構文的には!
が使われます。
つまり、「 ?
で曖昧に宣言したものを!
で確信に変える 」と考えると分かりやすいです。
(ゲームとかでなにか発見すると頭に「!」が出たりしますよね、あれですw)
未確定 と 確定
というのが、理解した内容です。
実際の使用例を見ると分かりやすいかもしれません。
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
に関しては、上記のイメージで使用できると思います。
ただ、宣言時と代入時にも若干の制約があります。
これは、宣言した型が持てる状態と、代入しようとしている状態をイメージするといいと思います。
つまりコードにすると以下のようになります。
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>
(おそらく)!
と?
は上記のシンタックスシュガー的なものかなと。(ただ、微妙に挙動は異なる)
上記をそれに置き換えると以下のようになる。
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