LoginSignup
9
8

More than 5 years have passed since last update.

Optional記号!, ?の使い方

Last updated at Posted at 2015-07-10
  • 宣言、ダウンキャスト、メソッド呼出、メソッド引数、左辺値、5つのケースでメモを残しておく。(Xcode7 beta1 + Swift2.0)
  • 宣言
foo.swift
var x:Int! = 1
var y:Int? = 2
print(x)
print(y)
//1
//Optional(2)
//x = nil
//y = nil
var z: Int = 3
//z = nil // compile error as below
//error: cannot assign a value of type 'nil' to a value of type 'Int'

//1)x is an Optional(nil acceptable), but already unwrapped
//2)y is also an Optional(nil acceptable), but wrapped(Optional 2)
// so method call such as y!.distanceTo(-2) needs "!" notation.
print(y!.distanceTo(-2))
print(x.distanceTo(-2))
print(x!.distanceTo(-2))
//print(z!.distanceTo(-2)) // 
//error: operand of postfix '!' should have optional type; type is 'Int'        

//3) z is not nil acceptable.
  • ポイント
  1. 記号!, ?何れかを使って宣言すれば、Optional(nil acceptable)と見なされる。
  2. Optional型には2つのstateがあって、一つはwrappedで、もう一つはunwrapped。
  3. 記号の有無は、メソッド呼出時に影響でる。メソッド呼出時にwrappedなインスタンスであれば記号!を使ってメソッド呼出が必要。Optional型であってもunwrappedなら記号!不要でメソッド呼出可能。
  4. Optional型で無いインスタンスに記号!を付けてメソッド呼出を行えば、コンパイルエラー発生。(例:上記のvar z)
  5. Optional型であっても既にunwrappedな上記のvar xであれば記号!無しにメソッド呼出可能。(ex: print(x.distanceTo(-2)))記号!があってもOK。(ex: print(x!.distanceTo(-2)))
  • ダウンキャスト
foo.swift
let delegateObj = anObject as! AppDelegate // (1)
//let delegateObj = anObject as? AppDelegate // (2)
let delegateObj = anObject as AppDelegate! // (3)
  • ポイント
  1. anObject変数のnil check無でnon-opptional型へ強制ダウンキャスト 【場合(1)】する。anObjectがnilならエラー発生する。
  2. anObject変数のnil check有りでnon-opptional型へ強制ダウンキャスト 【場合(2)】、ただしキャストに失敗してもエラー発生させない。(swift2.0ではコンパイルエラー)check失敗でも。。。というよりコンパイルエラー発生。
  3. opptional型へダウンキャスト 【場合(3)】、ただし、unwrap済。
  4. asキーワード(?, !も付かないas)はキャストアップへ使う。例えば、UITouchからNSObjectへのキャストへ使うが、あまり使い道が無い。
  • メソッド呼出
foo.swift
foo!.goToSchool() // (1)
foo?.goToSchool() // (2)
  • ポイント
  1. nil checkせずにfooインスタンスのメソッド呼出をする。check失敗時、nilに対しgoToSchool()メソッド呼出をするので、実行時にエラーが発生する。nil guardが必要。
  2. nil checkしてからfooインスタンスのメソッド呼出する。check失敗時、メソッド呼出しない。fooがnilでも実行時にエラー発生しない。
  • メソッド引数

宣言の時と同じ。

  • 左辺値
foo.swift
    self.canvas1 = self.canvasImage1()
//    self.canvas1! = self.canvasImage1() nil found error occured.
//    self.canvas1? = self.canvasImage1() blank is drawn.

  • ポイント
  1. 左辺値に用いて値代入する際には、unwrap済みである事一見で判る!?記号が無い状態で記述しないと、実行時にエラーとなるか、nil代入されちゃう。
  2. !記号付きで左辺値に記述すると、nil checkせずunwrapする。unwrapの結果nilなら定数nilへの代入は出来ないのでnil found実行時エラーが発生。unwrapの結果nilでないなら、変数の中身が上書きされる。

  3. ?記号付きで左辺値を記述すると、nil checkをしてからunwrapする。check失敗すると代入は実行されない。check成功するとunwrapされた変数の中身は右辺値で上書きされる。
    check失敗時、実行時エラーも発生せず、上記の例であればblankスクリーンが表示される。なお、canvasはUIImage型のオフスクリーン。スクリーンへcanvasが転送されても(drawInRect:rect)nilスクリーンが表示される。

  • メソッド名、変数名、as(キャスト記号)に!?記号を付ける場合、nil checkの有無を!?が表す。delegate.someMethod?()は委譲先でsomeMethod()が実装されている際(nil check成功)には呼び出すが、nil check失敗時メソッド呼出は行われない。

  • 変数名に!?が付いている場合、nil check後にunwrapする。

  • anObject as! AnObjectの意味は、Optional型であるanObject変数のnil check無でnon-Optional型のAnObjectへキャストする。(←これ間違い、swift1.2の場合)

  • Optional型のanObject変数をnon-OptionalなAnObjectへキャストする。anObject as AnObject!と同じ。anObject as? AnObjectはanObject as AnObject?と同じ。

  • 型名に!?記号を付ける場合(例えば、Int?とかInt!とか)、その型名で宣言された変数はunwrap済なのか、そうで無いのかを示す。

  • !?を変数、メソッドに付ける場合と、宣言、キャスト時に付ける場合を分けて考える。
    【上記、編集途中です。】
    参考になりそうなStackOverflowの記事

9
8
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
9
8