LoginSignup
49
50

More than 5 years have passed since last update.

理解があいまいな !型と?型の変数を違いを比較する

Posted at

swiftでの変数に ! や ?、および無印がありますが、いまいち理解があいまいだったのでPlaygroundで比較してみました。

Optional型がどうこうとか、そういう理論的な話は抜きです。
どういう場合にビルドエラーになるのか、どういう時に実行時エラーになるのかが中心です。

特に、普段使わない !型で宣言した変数の扱い について見ていきます。

長いので先に結論です

  • ?型変数 はnilの可能性がある。末尾に ? か ! を付けて適切に処理するように。
  • !型変数 はnilの可能性がある。nil だと落ちるので注意するように。
  • 無印はnilの可能性がないので安全に扱える

代入

まずは変数宣言と、単純な値の代入の実験です。

var hoge1 : String? = nil  //OK
var hoge2 : String! = nil  //OK
var hoge3 : String = nil //ビルドエラー

意外だったのは、 !がnil許容 だったことです。
ずっと !はnil非許容と勘違いしてました。


実行

次にnil許容な変数に対する何らかの操作を行った場合です。

//?
var hoge1 : String? = ...
hoge1.lowerCaseString //ビルドエラー
hoge1?.lowerCaseString  //String?型の値が返る
hoge1!.lowerCaseString  //String型が返る。nilだと実行時クラッシュ

//!
var hoge2 : String! = ...
hoge2.lowerCaseString //String型が返る。nilだと実行時クラッシュ

//無印はそもそもコンパイラの段階でnilを許容しないので省略

値の受け渡し

ちょっと複雑になります。次にそれぞれの型の値の受け渡しです。

?マークへの代入

var h1 : String? = ...
var h2 : String! = ...
var h3 : String = ...

var value : String?
value = h1    //OK。これはそのまま
value = h1?   //OK。?を付けても付けなくても一緒
value = h1!   //nilだと実行時エラー
value = h2    //OK。nilでも左辺がString?型だから問題なし
value = h3    //OK。確実に値が入る

!マーク

var h1 : String? = ...
var h2 : String! = ...
var h3 : String = ...

var value : String!
value = h1    //OK h1がnilだとvalueもnil
value = h1?   //OK h1がnilだとvalueもnil
value = h1!   //h1がnilだと実行時エラー
value = h2    //OK。h2がnilだとh1もnil
value = h3    //OK。確実に値が入る

無印

var h1 : String? = ...
var h2 : String! = ...
var h3 : String = ...

var value : String
value = h1    //ビルドエラー
value = h1?   //ビルドエラー
value = h1!   //nilだと実行時エラー
value = h2    //nilだと実行時エラー
value = h3    //OK


一覧表

表にしてまとめてみます
危険 は右辺がnilの場合に実行時例外を意味します
つまり、危険のパターンはif等で事前に判定しておく必要があります。

左辺\右辺 ?(生) ?(?つき) ?(!つき) !型 無印
?型= OK OK 危険 OK OK
!型= OK OK 危険 OK OK
無印= ビルド不可 ビルド不可 危険 危険 OK

関数呼び出し

関数呼び出しについても代入と同様です。

受け取り手のメソッドの引数が、?や無印のときは問題ないと思います。
?のときは ? や ! を付ける、無印のときはnilの可能性はないのでそのまま実行してOKです。

func test1(str : String?) {
    let lower = str?.lowerCaseString //?なり!なりつけるだけ
}
func test3(str : String) {
    let lowser = str.lowerCaseString //nilの可能性がない
    //逆に if str != nil と書くとビルドエラー。nilの可能性がないと言われる
}

一方、!はnilの可能性があるので ifでnilチェック する必要があります。

func test3(str : String!) {
    let lowser = str.lowerCaseString //もしstrがnilなら落ちる
    //よってこうする必要がある
    if str != nil {
        let lower = str.lowerCaseString
    }
}

正直なところ、自分で作るメソッドなら引数は ! ではなく ? のほうがいいと思います。
nilの可能性がないなら無印にすべきです。(普通作らないとは思いますけど...)

ではなぜ ! があるかというと、 Objective-C時代の遺産 が多いです。
delegate系に多く存在します。

当然ながらObj-CにOptional型なんてないので、 nilの可能性がある引数は 基本!付きになります
(※このためだけに!型が用意されたとしか思えない...)

Delegate等での!型引数の場合、基本的にnilチェックをしてください。
もしくは一旦 ?型の変数に入れてOptionalChaining にするのも手です。


まとめ

再びまとめです。
・ ?型変数 はnilの可能性がある。末尾に?か!を付けて適切に処理するように。
・ !型変数 はnilの可能性がある。nilだと落ちるので注意するように。
・ 無印 はnilの可能性がないので安全に扱える

?はどっちか分からん !は気をつけろ 無印は安全 と覚えると楽かもしれません。

49
50
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
49
50