こんにちは。きんくまです。
この間発表されたSwiftなんですけど、ちょっと触ってみてます。
それで、!とか?の役割がイマイチわかんなかったので、調べてみました。
今回の参考リンクです。
>> [Swift] Optional型についてのまとめ (Quiita)
>> What does an exclamation mark mean in the Swift language? (Stack Overflow)
上のリンクで最初の記事がわかりやすかったので、こちらを使って自分でもコードを書いてみます。
まず前準備で、クラスを作ります。
class Dog { func bark()->String { return "Wan!" } }
!も?もつかない、これまでの場合
// ? ! なしの状態 var pochi:Dog = Dog() pochi.bark() //-> "Wan!" #1 pochi?.bark() //コンパイルエラー optionalじゃないから #2 pochi!.bark() //コンパイルエラー optionalじゃないから #3 pochi = nil //コンパイルエラー #4
変数の宣言時に!も?もつかない場合は、普通にメソッドが呼び出せました。#1
#2と#3の場合、?と!を付けてメソッドを呼び出そうとすると、コンパイルエラーが出ました。
optionalじゃないのに、optionalの時のやり方をしようとしたからです。
nilを代入しようとしたらエラーです #4 。つまり、オプショナルな記号をつけない場合はnilは許容されません。値がある必要があります。
ちょっと脱線 インスタンスのポインタのvarとlet
var pochi:Dog = Dog() var pochi2:Dog = Dog() pochi2 = pochi let pochi3:Dog = Dog() pochi3 = pochi2 //コンパイルエラー
swiftでは変数の宣言にvarとletのキーワードがあります。varは宣言後に代入できますが、letは宣言時のみです。
文字列や数値では、letの変数に代入するとコンパイルエラーが出ます。
インスタンスのポインタにletを使った場合は、別のポインタを代入するとコンパイルエラーになりました。
上の例でも、varの場合は別のポインタをつっこめましたが、letの場合は駄目でした。
なので、インスタンス化する場合のポインタは、使い回さない限り、他のポインタを代入できないletを使っておいた方がいいみたい。
? つきの変数
話を戻して ? つきの変数の挙動です。
// ?つき var john:Dog? = Dog() john.bark() //コンパイルエラー #1 //unwrap john!.bark() //-> "Wan!" #2 john?.bark() //-> {Some "Wan!"} #3 //#4 if let j = john { println(j.bark()) //-> "Wan" } john = nil //OK #5 //nilの状態でやってみる john!.bark() //ランタイムエラー #6 john?.bark() //nil #7 //#8 if let j = john { println(j.bark()) //ここは通らない }
?がついている場合は、さきほどと違ってnilになっても大丈夫です。#5
またこのときwrapの状態の変数になっています。wrapの状態とは「中身が何かわかんないけど、箱として存在している」というものみたいです。
なので、中身が何かわかんない状態なので、Dog型にbarkっていうメソッドが存在しているかコンパイラはわからないので、エラーになります。#1(Dog型に?つきで宣言しているのに、、)
なので、そういう場合に、箱を空けてあげます(unwrap)。
unwrapの方法は上のリンクのstackoverflowの記事によると3つあります。
1. forced unwrapping
! をつけてその型にキャストする方法
上のコードだと #2、#6 です。
キャストするので、もし中身がnilだった場合 #6 は、ランタイムエラーになります。
2. optional chaining
? をつけてその型だと思ってbarkを呼び出す方法
上のコードだと #3、#7 です
optional chainingはその値がnilだったら、その場でnilを返すやり方みたい。
さっきの #6 はランタイムエラーになったのに、#7の場合はnilが返ってそのままプログラムは続きます。
また、Playground環境(プロジェクトの一種)だと吐き出される値も、#2の場合はそのまま返ってきているのに、#3の場合はSomeとつけているので、キャストして、完全にその型に変換しているというよりは、そいういうメソッドがあると仮定して呼び出すみたいなイメージだと思われます。
ちなみにprintlnして、Consoleで見ると #2 も #3 もどちらとも「Wan!」って出力されます。(Someってつかない)
また、optional chaining っていうぐらいなので、
foo?.bar()?.hoge っていう感じに、nilにならない限りメソッドやプロパティを連結できるのも特徴です。
>> Optional Chaining(Appleのドキュメント)
3. optional binding
上のコードだと #4, #8 です
optional bindingは、Appleのドキュメントによると、if文とwhile文で使えるやり方で、
「条件式部分に、変数を宣言して代入、そのとき、その変数に値があったら、その中身を実行(中でその変数をそのまま使える)」というものみたい。
! つきの変数
// !つき var hachi:Dog! = Dog() hachi.bark() //OK -> "Wan!" #1 //unwrap hachi!.bark() //-> "Wan!" #2 hachi?.bark() //-> {Some "Wan!"} #3 hachi = nil //OK #4 //nilの状態でやってみる hachi.bark() //ランタイムエラー #5 hachi!.bark() //ランタイムエラー #6 hachi?.bark() //nil #7
!つきの場合は、?つきの場合と同じところと、いくつか違うところがありました。
まず、nilは代入できます #4
?つきでは、コンパイルエラーになっていた そのままメソッドを呼び出す方法はエラーになりません。#1, #5
ただ、#5 ではnilのメソッドを呼び出そうとするので、ランタイムエラーになります。
unwrapのときは、?つきと全く同じ結果になりました。#2, #3, #6, #7
まとめ
以上のことからまとめると、
変数がnilじゃなく値があるとき
変数の宣言 | 普通に呼び出し(foo.bar) | forced unwrapping (foo!.bar) | optional chaining(foo?.bar) | nilの代入(foo = nil) |
---|---|---|---|---|
foo | OK | コンパイルエラー | コンパイルエラー | コンパイルエラー |
foo? | コンパイルエラー | OK | OK {Some} | OK |
foo! | OK | OK | OK {Some} | OK |
変数がnilのとき
変数の宣言 | forced unwrapping (foo!.bar) | optional chaining(foo?.bar) |
---|---|---|
foo? | ランタイムエラー | nilが返る |
foo! | ランタイムエラー | nilが返る |
もともとはUITableViewDataSourceで、
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int
となっていて、引数のtableView: UITableView! の最後の! は何だ?というところから調べてみました。
変数の後ろに! がついている場合は、表を見るとわかるように ついてない場合の上位互換みたいなもので、nilが中身に入っていてもOKだし、unwrapもできるというものでした。
■ 自作iPhoneアプリ 好評発売中!
・フォルメモ - シンプルなフォルダつきメモ帳
・ジッピー電卓 - 消費税や割引もサクサク計算!
■ LINEスタンプ作りました!
毎日使える。とぼけたウサギ