値付き Enum 入門、そして伝説へ #yhios #cocoa kansai
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share

値付き Enum 入門、そして伝説へ #yhios #cocoa kansai

  • 0 views
Uploaded on

Swift の新機能『値付き列挙型』について、列挙型の基礎から、Swift 自身での応用例まで徹底的に掘り下げてみました。

Swift の新機能『値付き列挙型』について、列挙型の基礎から、Swift 自身での応用例まで徹底的に掘り下げてみました。

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
0
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
0
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. 値付き enum 入門 ~ そして伝説へ ~ EZ-NET 熊谷友宏 http://ez-net.jp/ 2014.11.29 @ 横浜へなちょこiOS勉強会 #34.1 2014.12.06 @ 第59回 Cocoa 勉強会関西
  • 2. 自己紹介 EZ-NET 熊谷友宏 @es_kumagai Xcode 5 徹底解説 IP Phone 音でダイヤル音で再配達ゴッド いつもの電卓 for iPhone いつもの電卓 for iPad 音で再配達
  • 3. 列挙型 ― いよいよ enum は究極形態へ ―
  • 4. 普通の列挙型 列挙型
  • 5. 普通の列挙型 列挙型 定義 Swift enum Basket { case Empty case Pineapple case Melon } 1. 限られた候補の中から値を採るデータ型 2. 意味で値をひとまとめにできる 3. 値を言葉で扱えるのでコードが明瞭に
  • 6. 普通の列挙型 列挙型 使い方 Swift let value:Basket switch value { case .Pineapple: … case .Melon: … case .Empty: … }
  • 7. やや新しい列挙型 列挙型
  • 8. やや新しい列挙型 列挙型 Raw 値の割り当て 定義 enum Basket : Int { case Empty = 0 case Pineapple = 1 case Melon = 2 } 1. 列挙子に内部的な値を付与 2. Objective-C では必ず Raw 値が設定される 3. Swift で Raw 値がない列挙子は具体値なし
  • 9. やや新しい列挙型 列挙型 列挙子から Raw 値を取得 Raw 値を取得 let raw = Basket.Melon.rawValue Raw 値から列挙子を生成 列挙子を生成 let value = Basket(rawValue: raw)!
  • 10. まったく新しい列挙型 列挙型
  • 11. まったく新しい列挙型 列挙型の新機能 1. 列挙型もオブジェクト プロパティやメソッドを実装可能 2. 整数値以外を Raw 値で使える リテラル値から変換できる型を利用可能 3. 列挙子と合わせて値を持てる 複数の値を自由に保有可能 ? ?
  • 12. まったく新しい列挙型 1. 列挙型もオブジェクト
  • 13. まったく新しい列挙型 プロパティやメソッドを実装可能
  • 14. まったく新しい列挙型 列挙子を文字列に変換するメソッド メソッドの実装 enum Basket { case Empty, Pineapple, Melon func toString() -> String? { switch self { case .Empty: return nil case .Pineapple: return "Pineapple" case .Melon: return "Melon" } }
  • 15. まったく新しい列挙型 列挙型の変数からメソッドを実行したり 変数から実行 let basket = Basket.Melon let string = basket.toString() 列挙子から直接メソッドを実行したり 列挙子から実行 let string = Basket.Melon.toString()
  • 16. まったく新しい列挙型 2. 整数値以?外を Raw 値で使える
  • 17. まったく新しい列挙型 実際に使ってみると…
  • 18. まったく新しい列挙型 エラー 『Raw 型がどのリテラルからも変換できません』
  • 19. まったく新しい列挙型 リテラル値から変換できる型を 利用できるかと思えば …
  • 20. まったく新しい列挙型 利用できないものが多い
  • 21. よくあるリテラル 1. 整数値リテラル … 0 IntegerLiteralConvertible 2. 浮動小数点数値リテラル … 0.0 FloatLiteralConvertible 3. 真偽値リテラル … true BooleanLiteralConvertible 4. 文字列リテラル … "STRING" StringLiteralConvertible [OK] [OK] [NG] [OK] まったく新しい列挙型
  • 22. これもリテラル 1. nil リテラル … nil NilLiteralConvertible 2. 配列リテラル … [ value, ... ] ArrayLiteralConvertible 3. 辞書リテラル … [ key : value, ... ] DictionaryLiteralConvertible [NG] [NG] [NG] まったく新しい列挙型
  • 23. 使えるものに注目すれば … 1. 整数値リテラル 2. 浮動小数点数値リテラル 3. 文字列リテラル まったく新しい列挙型
  • 24. まったく新しい列挙型 列挙子の値が… 文字列 でも良い
  • 25. まったく新しい列挙型 Raw 値を文字列型にする… 定義 enum Basket : String { case Empty = "" case Pineapple = "Pineapple" case Melon = "Melon" } 1. 内部的な値を文字列で付与 2. 列挙子と文字列値とが関連付けられる
  • 26. まったく新しい列挙型 Raw 値として文字列を取り出せる Raw 値の取得 let string:String = Basket.Melon.rawValue 文字列から列挙子を生成することも可能 列挙子の生成 let value:Basket? = Basket(rawValue: "Melon")
  • 27. 列挙子の型が… まったく新しい列挙型 リテラルから変換 できれば良い 対応する ― それと等価判定もできること ―
  • 28. まったく新しい列挙型 対応するリテラル 1. 整数値リテラル IntegerLiteralConvertible 2. 浮動小数点数値リテラル FloatLiteralConvertible 3. 文字列リテラル StringLiteralConvertible ― 等価判定は Equatable ―
  • 29. まったく新しい列挙型 対応リテラルからの変換機能を実装 クラスを浮動小数点数値リテラルに対応させる プロトコルへ準拠 class MyClass : FloatLiteralConvertible, Equatable { // 変換イニシャライザ(浮動小数点数から) required init(floatLiteral value: FloatLiteralType) { } } // 等価演算子 func ==(lhs:MyClass, rhs:MyClass) -> Bool { return true }
  • 30. まったく新しい列挙型 列挙型で独自クラスを使用 浮動小数点数値リテラルに対応したクラス 列挙型の定義 enum Basket : MyClass { case Empty = 0.0 case Pineapple = 1.0 case Melon = 1.1 } 1. Raw 値の型に独自クラスを使用可能 2. Raw 値はリテラルで指定
  • 31. 扱い方は普段どおり 普通に使う分には Raw 値は意識しない 列挙型の使用 let basket:Basket = .pineapple switch basket { case .Pineapple: … case .Melon: … case .Empty: … }
  • 32. まったく新しい列挙型 Raw 値の独自型は 何時、使われるのかいつ
  • 33. まったく新しい列挙型 要点1 列挙子を使う分には… 1. 独自型は 全く使われない 2. 列挙子の操作では リテラル が使われる 使用 let basket:Basket = .pineapple switch basket { case .Pineapple: … case .Melon: … }
  • 34. まったく新しい列挙型 要点2 Raw 値を取得したとき… 1. 初めてインスタンスが生成される 2. 変換イニシャライザ が実行される 3. 列挙子が対応するリテラルが渡される Basket.Melon.rawValue MyClass(floatLiteral: 1.1) MyClass
  • 35. まったく新しい列挙型 要点3 Raw 値から列挙型を生成するとき… 1. Raw 値から インスタンスを生成 2. 列挙子を 順次 インスタンス化して比較 3. 戻り値は MyEnum? 型 4. 該当しない場合は nil を返す
  • 36. let obj = MyClass(floatLiteral: 1.0) MyEnum(rawValue: obj) MyClass(floatLiteral: 0.0) == obj MyClass(floatLiteral: 1.1) == obj MyEnum? MyClass(floatLiteral: 1.0) == obj case .Empty case .Pineapple case .Melon
  • 37. まったく新しい列挙型 要点4 リテラルから列挙子を作るときも… 1. いったん 独自型に変換 される 2. 列挙子を 順次 インスタンス化して比較 MyEnum(rawValue: 1.0) MyEnum(rawValue: MyClass(floatLiteral: 1.1)) MyEnum?
  • 38. まったく新しい列挙型 つまり独自型の Raw 値は… 1. 使い出すとハイコスト その都度インスタンス化が行われる 2. 使わなければリテラルと同等 列挙子自体は独自型では管理されない ― 列挙子とインスタンスを関連付ける的な役割 ―
  • 39. まったく新しい列挙型 3. 列挙子?と合わせて値を持てる
  • 40. 値付き列挙型 associated values ― 値を持てる列挙型 ―
  • 41. 値付き列挙型 値付き列挙型 定義 Swift enum Basket { case Empty case Fruit(String) case Animal(String) } ― 列挙子にデータ型を添えて定義 ―
  • 42. 値付き列挙型 値付き列挙型 列挙子を使う 宣言と代入 let basket1 = Basket.Fruit("りんご") let basket2 = Basket.Animal("ライオン") let basket3 = Basket.Empty ― 値付き列挙子には値を必ず添える ―
  • 43. 値付き列挙型 値付き列挙型 値を加味した篩い分け 分岐 let basket:Basket switch basket { case .Fruit("みかん"): … case .Fruit("りんご"): … default: … } ― 列挙子と値での篩い分けが可能 ―
  • 44. 値付き列挙型 値付き列挙型 大まかな篩い分け 分岐 let basket:Basket switch basket { case .Fruit: … case .Animal: … case .Empty: … } ― 列挙子だけでの篩い分けも可能 ―
  • 45. 値付き列挙型 値を加味したり、しなかったり 分岐 let basket:Basket switch basket { case .Fruit("みかん"): … case .Fruit: … default: … } ― 自由に混在可能 ― 値付き列挙型
  • 46. 値付き列挙型 値付き列挙型 値を取り出して利用する 分岐・値の取得 let basket:Basket switch basket { case let .Fruit(fruit): println("(fruit)!") } ― 列挙子が一致したとき、その値を取り出す ―
  • 47. 複数の値も付けられる 値付き列挙型
  • 48. 値が複数付いた列挙型 定義 Swift enum Basket { case Empty case Fruit(String, Int) case Animal(String, Int, Bool) } ― タプル型で列挙子の値を定義 ― 値付き列挙型
  • 49. 値付き列挙型 値が複数付いた列挙型 値を取り出して利用する 分岐と各値の判定・取得 let basket:Basket switch basket { case let .Fruit("りんご", price): println("りんごは特売! (price)円!") case let .Fruit(name, price): println("(name)が(price)円!") } ― 値を個別に取り出せる、どれか固定も可能 ―
  • 50. 値が複数付いた列挙型 値を一括で取り出すことも可能 分岐と値の一括取得 let basket:Basket switch basket { case let .Fruit(value): println("(value.0)が(value.1)円!") } ― 値をタプル型で取り出せる ― 値付き列挙型
  • 51. 値付き列挙型 値が複数付いた列挙型 名前付きタプルも利用可能 定義 enum Basket { } 使用 case Empty case Fruit(name: String, price: Int) switch basket { case let .Fruit(value): println("(value.name)が(value.price)円!") }
  • 52. ジェネリックな 値付き列挙型 ― 汎用的な値を持つ ―
  • 53. ジェネリックな値付き列挙型 ジェネリックとは Generics 汎用的に型を扱う仕組み ― 型に縛られないコードが書ける ― ジェネリック関数 func makePair<T,U>(first:T, second:U)->(T,U) { return (first, second) }
  • 54. ジェネリックな値付き列挙型 ジェネリックな値付き列挙型 定義 Swift enum Basket<Type> { case Empty case Fruit(Type) case Animal(String) } 仕様上 NG ジェネリックを値に持つ列挙型で 複数の値付き列挙子を定義できない
  • 55. ジェネリックな値付き列挙型 ジェネリックな値付き列挙型 定義 Swift enum Basket<Type> { case Empty case Nandemo(Type) } ― 列挙子が採る値の型を汎用化 ―
  • 56. ジェネリックな値付き列挙型 ジェネリックな値付き列挙型 型に縛られない列挙型の使用 宣言と代入 let basket1:Basket<String> = Basket.Nandemo("りんご") let basket2:Basket<Int> = Basket.Nandemo(1000) 1. 渡した値に応じて列挙型が決まる 2. 宣言以降は値の型を変えられない 3. あくまでもコードの汎用化が目的
  • 57. ジェネリックな値付き列挙型 ジェネリックな値付き列挙型 扱い方は通常と同じ 判定 switch basket { case let .Nandemo(value): … case .Empty: … } ― 値の型は宣言時に決めた型 ―
  • 58. ジェネリックな値付き列挙型 ところで…
  • 59. ジェネリックな値付き列挙型 ジェネリックな値付き列挙型 似たコンセプトの列挙型… この列挙型と、 enum Basket<Type> { case Empty case Nandemo(Type) } この列挙型と。 enum Optional<T> { case None case Some(T) }
  • 60. ジェネリックな値付き列挙型 どこかで見覚えのある列挙型 enum Optional<T> { case None case Some(T) }
  • 61. そして伝説へ…
  • 62. Optional<T> ― nil を持てる型 ― そして伝説へ…
  • 63. そして伝説へ… Optional<T> オプショナルとは? 1. 任意の値を格納できる型 2. 値の型は宣言時に決定 3. 値がない状態を “nil” で表現可能 4. Swift 言語の重要な機能
  • 64. Swift の Optional って String? そして伝説へ…
  • 65. シンタックスシュガー ― 簡単に書くための構文 ―
  • 66. String? is シンタックスシュガー Optional<String> ― 明示的な扱いが必要な nil 許容型 ―
  • 67. そして伝説へ… Optional<T> Swift で定義されている
  • 68. そして伝説へ… Optional<T> 定義から見る特徴 1. ジェネリックな値付き列挙型 2. ある値 .Some(T) と 何もない値 .None を採る 3. 引数名なしで値を採るイニシャライザ init(_ some:T) 4. nil リテラルから変換可能 NilLiteralConvertible
  • 69. ジェネリックな値付き列挙型 シンタックスシュガー ということは
  • 70. シンタックスシュガー この2つは同じことを表現 嘘のような本当の話 シンタックスシュガー let str1:String? = "STRING" let str1:String? = nil 列挙型による実装 let str2:Optional<String> = .Some("STRING") let str2:Optional<String> = .None
  • 71. シンタックスシュガー 同じことなので混在も可能 表現は違っても値は同じ シンタックスシュガーと列挙型の混在 // Optional<T> に T? のときと同じ表現で代入 let str1s:Optional<String> = "STRING" let str1n:Optional<String> = nil // String? に enum Optional<T> の書式で代入 let str2s:String? = .Some("STRING") let str2n:String? = .None // String? を Optional<T> のイニシャライザで生成 let str3s:String? = Optional<String>("STRING") let str3n:String? = Optional<String>()
  • 72. シンタックスシュガー 実際の動きを知ると Optional が分かりやすくなる
  • 73. シンタックスシュガー 値を Optional でラップする Optional 型に値を設定 シンタックスシュガー // 型の最後に「?」を付ける let str:String? = "STRING" 列挙型による実装 // 列挙型 Optional.Some の値として指定する let str2 = Optional.Some("STRING") // Optional<T> のイニシャライザでも良い let str2 = Optional("STRING")
  • 74. シンタックスシュガー Optional に nil を代入する Optional 型に nil を設定 シンタックスシュガー // 型の最後に「?」を付けた変数に nil を代入する let str:String? = nil 列挙型による実装 // Optional<T>.None を代入する(要・型の明示) let str2 = Optional<String>.None // Optional<T> のイニシャライザでも良い let str2 = Optional<String>()
  • 75. シンタックスシュガー 値が nil かを判定する 値があるかないかを if で判定 シンタックスシュガー if str != nil { } else { } 列挙型による実装 switch str { case .Some: … case .None: … }
  • 76. シンタックスシュガー 値が nil かを判定する Optional Binding で値を取得 シンタックスシュガー if let value = str { } else { } 列挙型による実装 switch str { case let .Some(value): … case .None: … }
  • 77. シンタックスシュガー Optional から値を取り出す 強制アンラップ シンタックスシュガー let value = str! 列挙型による実装 var value:String switch str { case let .Some(v): value = v case .None: abort() }
  • 78. シンタックスシュガー Optional から値を取り出す nil 結合演算子(右辺が nil 非許容のとき) シンタックスシュガー let value = str ?? "DEFAULT" 列挙型による実装 var value:String switch str { case let .Some(v): value = v case .None: value = "DEFAULT" }
  • 79. シンタックスシュガー Optional から値を取り出す nil 結合演算子(右辺が nil 許容のとき) シンタックスシュガー let value = str ?? Optional("DEFAULT") 列挙型による実装 var value:String? switch str { case let .Some: value = str! case .None: value = Optional("DEFAULT") }
  • 80. シンタックスシュガー Optional から値を取り出す Optional Chaining で値を取得 シンタックスシュガー let value = str?.hashValue 列挙型による実装 var value:Int? switch str { case let .Some(v): value = v.hashValue case .None: value = nil }
  • 81. ! も ? も ?? も バリエーション nil かどうかで「どうするか」 が少し違うだけ
  • 82. シンタックスシュガー シンタックスシュガーのおかげで Optional を簡単に扱える ― switch 文を省略できる ―
  • 83. ちなみに
  • 84. Optional<T> には func map<U>(f: (T)->U) -> U? 1. 値が nil なら nil を返す 2. 値が nil でなければ、引数で渡された クロージャの実行結果をラップして返す
  • 85. シンタックスシュガー Optional から値を取り出す Optional<T>.map<U> メソッドで実行 シンタックスシュガー let value = str?.hashValue Optional 型による実装 // いちばん短い書き方 let value = str.map { $0.hashValue } // 省略しない書き方 let value = str.map({ (value:String) -> Int in return value.hashValue })
  • 86. もうひとつ
  • 87. ImplicitlyUnwrappedOptional<T> ― Optional の姉妹品 ―
  • 88. String! is シンタックスシュガー ImplicitlyUnwrappedOptional<String> ― 暗黙的にアンラップされる nil 許容型 ―
  • 89. シンタックスシュガー ImplicitlyUnwrappedOptional 値のラップは Optional と同等 シンタックスシュガー // 型の最後に「!」を付ける let str:String! = "STRING" 列挙型による実装 // 列挙型 .Some の値として指定する let str2 = ImplicitlyUnwrappedOptional.Some("STRING")
  • 90. シンタックスシュガー ImplicitlyUnwrappedOptional nil の設定は Optional と同等 シンタックスシュガー // 型の最後に「!」を付けた変数に nil を代入する let str:String! = nil 列挙型による実装 // .None を代入する(要・型の明示) let str2 = ImplicitlyUnwrappedOptional<String>.None
  • 91. シンタックスシュガー ImplicitlyUnwrappedOptional 値の nil 判定は Optional と同等 シンタックスシュガー if str != nil { } else { } 列挙型による実装 switch str { case .Some: … case .None: … }
  • 92. シンタックスシュガー ImplicitlyUnwrappedOptional Optional Binding は存在しない ( if let v = str ) ― 暗黙アンラップが基本 ―
  • 93. シンタックスシュガー ImplicitlyUnwrappedOptional アンラップは型が明確なら暗黙的に実施 シンタックスシュガー let value = str! // 明示アンラップ let value:String = str // 暗黙アンラップ 列挙型による実装 var value:String switch str { case let .Some(v): value = v case .None: abort() }
  • 94. シンタックスシュガー ImplicitlyUnwrappedOptional nil 結合演算子は Optional と同等(nil 非許容) シンタックスシュガー let value = str ?? "DEFAULT" 列挙型による実装 var value:String switch str { case let .Some(v): value = v case .None: value = "DEFAULT" }
  • 95. シンタックスシュガー ImplicitlyUnwrappedOptional nil 結合演算子は Optional と同等(nil 許容) シンタックスシュガー // 結果は Optional<T> で得られる let value = str ?? Optional("DEFAULT") 列挙型による実装 var value:String? switch str { case let .Some: value = str! case .None: value = Optional("DEFAULT") }
  • 96. シンタックスシュガー ImplicitlyUnwrappedOptional Optional Chaining は無くて暗黙アンラップ シンタックスシュガー //「?」はなく即時アンラップ(nil なら強制終了) let value = str.hashValue 列挙型による実装 var value:Int switch str { case let .Some(v): value = v.hashValue case .None: abort() }
  • 97. シンタックスシュガー ImplicitlyUnwrappedOptional も Optional と僅かに違うだけ 1. 明示的/暗黙的アンラップ 2. Optional Binding の有無 3. Optional Chaining の有無 str! if let v = str str?.hashValue
  • 98. 仕組みを意識すると Optional って意外とシンプル ― 列挙型のシンタックスシュガー ―
  • 99. 値付き enum 入門 列挙型の基礎から 以上 Swift での実用例までのお話でした
  • 100. 『値付き enum 入門』 1. 普通な列挙型 case Pineapple, Melon, Empty 2. Raw 値を持てる列挙型 enum Basket : MyClass { 3. 列挙子ごとに値を持てる列挙型 case Fruit(String) 4. ジェネリックな値付き列挙型 enum Basket<T> { 5. Optional も列挙型 enum Optional<T> {