woshidan's blog

あいとゆうきとITと、とっておきの話。

はてな教科書の「プログラミング言語 Swift」を読んでます 6

https://github.com/hatena/Hatena-Textbook/blob/master/swift-programming-language.md

のんびり読んでいきます。もうちょい!

Casting

isでインスタンスの型を確認.

 84> class Cat: Animal { 
 85.     // 
 86.     // A'(A をオーバーライド。ここに required 修飾子がないと、サブクラス PersianCat に、このイニシャライザのオーバーライドを強制できない) 
 87.     // 
 88.     required init() { 
 89.         super.init() 
 90.     } 
 91. }
 92> class PersianCat: Cat {}
 93> var persianCat = Per
Available completions:
  PermutationGenerator
  PersianCat
 93> var persianCat = PersianCat()
persianCat: PersianCat = {
  __lldb_expr_47.Cat = {
    __lldb_expr_41.Animal = {
      name = "unknown"
    }
  }
}
 94> persianCat is Cat
$R8: Bool = true

asでキャストだが、スーパークラスにキャストする場合は、 as, サブクラスにキャストする場合は、as?(Optionalで失敗したらnil)as!(キャスト失敗でランタイムエラー)になります。

 95> persianCat as Cat
$R9: PersianCat = {
  __lldb_expr_47.Cat = {
    __lldb_expr_41.Animal = {
      name = "unknown"
    }
  }
}
 96> var cat = Cat()
cat: Cat = {
  __lldb_expr_41.Animal = {
    name = "unknown"
  }
}
 97> cat as? PersianCat
$R10: PersianCat? = nil
 98> cat as! PersianCat
Could not cast value of type 'Cat' (0x101336ab8) to 'PersianCat' (0x101336fe8).
Execution interrupted. Enter code to recover and continue.
Enter LLDB commands to investigate (type :help for assistance.)

どちらかというと、キャストに失敗したらelseみたいな例文が自信ないです...。

if let swift = source as? SwiftFile {
    compiled.append(swift.compile())
} else {
    compiled.append(source)
}
 128> if cat as? PersianCat { 
 129.     print("persian") 
 130. } 
repl.swift:128:8: error: optional type 'PersianCat?' cannot be used as a boolean; test for '!= nil' instead
if cat as? PersianCat {
       ^
   ((                ) != nil)

 128> if let persia = cat as? PersianCat { 
 129.     print("persian") 
 130. } 

letなどブロックの中で使う変数の定義が条件式に入った場合のif文がよくわからないみたいなので気をつけてみようと思います。

Protocol

いわゆるインタフェース。

  • プロパティ
  • メソッド
  • イニシャライザ
  • subscript*1

の宣言ができます。

また、メソッドは、 { get }, { get, set }で可変かを指定できます。

また、Protocol同士も継承できます。

  1> protocol Man { 
  2.     var firstName: String { get } 
  3.     var lastName: String { get } 
  4.     init(firstName: String, lastName: String) 
  5.     func printFullName()
  6. } 
  7> struct Japanese : Man { 
  8.     var firstName: String 
  9.     var lastName: String
 10.     init(firstName: String, lastName: String) { 
 11.         self.firstName = firstName
 12.         self.lastName = lastName 
 13.     } 
 14.     func printFullName() { 
 15.         print("\(firstName)\(lastName)") 
 16.     } 
 17. } 
 18> struct American : Man { 
 19.     var firstName: String 
 20.     var lastName: String 
 21.     init(firstName: String, lastName: String) { 
 22.         self.firstName = firstName 
 23.         self.lastName = lastName 
 24.     } 
 25.     func printFullName() { 
 26.         print("\(lastName) \(firstName)") 
 27.     } 
 28. } 
 29> var Taro = Japanese(firstName: "Yoshida", lastName: "Taro") 
Taro: Japanese = {
  firstName = "Yoshida"
  lastName = "Taro"
}
 30> var Ken = American(firstName: "Yoshida", lastName: "Ken")
Ken: American = {
  firstName = "Yoshida"
  lastName = "Ken"
}
 31> Taro.printFullName()
YoshidaTaro
 32> Ken.printFullName()
Ken Yoshida

 33> struct Arian : Man { 
 34.     var firstName: String { set {} } 
 35.   
 36.     init(firstName: String, lastName: String) { 
 37.         self.firstName = firstName 
 38.         self.lastName = lastName 
 39.     } 
 40.     func printFullName() { 
 41.         print("\(lastName) \(firstName)") 
 42.     } 
 43. } 
repl.swift:34:29: error: variable with a setter must also have a getter
    var firstName: String { set {} }
                            ^

Protocolは複数継承させることができますが、複数の protocol に適合していることを protocol<SomeProtocol, AnotherProtocol> と表現できるそうです。

 33> Taro as? protocol<Man>
$R0: Man? = (payload_data_0 = 0x0000000100509980 -> 0x00000001004fdde0 , payload_data_1 = 0x0000000000000000, payload_data_2 = 0x0000000000000000, instance_type = 0x00000001004f1218 nominal type descriptor for __lldb_expr_6.Japanese + 72, protocol_witness_0 = 0x00000001004f1110 protocol witness table for __lldb_expr_6.Japanese : __lldb_expr_4.Man in __lldb_expr_6) {
  payload_data_0 = 0x0000000100509980 -> 0x00000001004fdde0 
  payload_data_1 = 0x0000000000000000
  payload_data_2 = 0x0000000000000000
  instance_type = 0x00000001004f1218 nominal type descriptor for __lldb_expr_6.Japanese + 72
  protocol_witness_0 = 0x00000001004f1110 protocol witness table for __lldb_expr_6.Japanese : __lldb_expr_4.Man in __lldb_expr_6
}
 34> protocol Animal {}
 35> Taro as? protocol<Man, Animal> 
$R1: protocol<Animal, Man>? = nil

Extension

メタプロらしいものです。

computed プロパティやメソッド、イニシャライザや subscript を追加できますが、storedプロパティは追加できません。

 36> extension Int { 
 37.     var hex: String { 
 38.         return String(self, radix: 16) 
 39.     } 
 40. } 
 41> 245.hex
$R2: String = "f5"

また、extension は protocol も拡張でき、Protocol Extensionsといいます。

エラー処理

  • 例外を発生するメソッドに throws をつける
  • 例外を投げる時は、 throw
  • 例外は、ErrorTypeを継承した自分のクラスで定義できる
  • エラーを投げる関数の前に try をつけて、その関数を do { } catch ErrorType { } ...ブロックで囲む
    • 実際にエラーが発生しないことがわかっている場合は try! にしてエラーハンドリングしないことを明示できる(ただし実際に発生したらランタイムエラーになる)
    • try?にすると返り値がオプショナルになって、例外が発生した時はnilが返る
    • キャッチされなかったエラーはそのブロックの呼び出し元に伝播していきます
  • エラーを throws するクロージャの案件はおかわりで...
 42> enum SomeError : ErrorType { 
 43.     case Empty 
 44.     case Numeric 
 45. } 
 46> func throwError(str: String) throws -> String { 
 47.     if str.isEmpty { 
 48.         throw SomeError.Empty
 49.     } 
 50.     if let num = Int(str) { 
 51.         throw SomeError.Numeric 
 52.     } 
 53.     return str
 54. } 
 55> do { 
 56.   let res = try throwError("") 
 57.   print(res) 
 58. } catch SomeError.Empty { 
 59.     print("Empty") 
 60. } catch SomeError.Numeric { 
 61.     print("Numeric") 
 62. }
Empty
 63> do { 
 64.   let res = try throwError("123") 
 65.   print(res) 
 66. } catch SomeError.Empty { 
 67.     print("Empty") 
 68. } catch SomeError.Numeric { 
 69.     print("Numeric") 
 70. }
Numeric
 71> do { 
 72.   let res = try throwError("abc") 
 73.   print(res) 
 74. } catch SomeError.Empty { 
 75.     print("Empty") 
 76. } catch SomeError.Numeric { 
 77.     print("Numeric") 
 78. }
abc

defer

関数の最後に必ず実行したい処理がある場合、defer 文を利用することができます。

 79> func deferTest(str: String) { 
 80.     defer { 
 81.         print(str) 
 82.     } 
 83.     print(str + str + str) 
 84. } 
 85> deferTest("ABCD!")
ABCD!ABCD!ABCD!
ABCD!
// 途中でreturnする場合...?
 86> func deferTest2(str: String) { 
 87.     defer { 
 88.         print(str) 
 89.     } 
 90.     return
 91.     print(str + str + str) 
 92. } 
 93> deferTest2("ABCD!")
ABCD!ABCD!ABCD!
ABCD!
// 途中で例外を投げる場合
 94> func deferTest2(str: String) throws { 
 95.     defer { 
 96.         print(str) 
 97.     } 
 98.     throw SomeError.Empty
 99.     print(str + str + str) 
100. } 
 101> deferTest2("ABCD!") 
ABCD!
warning: could not load any Objective-C class information from the dyld shared cache. This will significantly reduce the quality of type information available.
 102>  

*1:ArrayやDictionaryのように[]の記法でクラスに値を変えさせるあれ