Kotlinに超入門してみたので続いてSwiftに超入門してみる。
超入門に使用する書籍は以下。
[改定新版]Swift実践入門-直感的な文法と安全性を兼ね備えた言語 WEB+DB PRESS plus
Playgroundsを使って確認しながら各単元の何かを書いてみる。
変数と定数
変数はvar、定数はletで宣言する。Kotlinと同様に宣言時に型情報が決まる必要がある。
つまり、変数(定数)宣言時に型アノテーションを書くか、初期値の型から型推論させるか。
型推論が出来るので、右辺から型が決まる場合には左辺の型定義(型アノテーション)は省略可。
1 2 3 4 |
let i: Int = 123 let j = 123 print(type(of:i)) // Int print(type(of:j)) // Int |
nilは代入不可。使用時までに値を入れている必要あり。
1 2 3 4 5 |
let i: Int = nil // Nil cannot initialize specified type 'Int' let j: Int j = nil // Nil cannot be assigned to type 'Int' let v: Int print(v) // Constant 'v' used before being initialized |
スコープ
外側の宣言は内側の宣言で使える。逆はできない。特別な予約語は不要。普通。
1 2 3 4 5 6 |
let globalint: Int = 100 func hoge() { lef localint: Int = 200 print(globalint) // 100 print(localint) // 200 } |
Bool型,リテラル
Bool型のリテラルはtrue,false。
演算子は論理和、論理積、否定のみ。普通。
1 2 3 4 5 |
let a: Bool = true let b: Bool = false print (a && b )// false print (a || b )// true print (!a) // false |
Int型,リテラル
IntはInt8,Int16,Int32,Int64。
32bitプラットフォームでIntはInt32、64bitだとInt64。普通。
Float型,Double型,リテラル
32bitがFloat、64bitがDouble。
Float,Doubleは無限(isInfinite)と非数値(isNaN)という状態をStaticプロパティとして持つ。
1 2 3 4 |
let a: Double = 10.0 / 0.0 a.isInfinite // true let b: Double = 0.0 / 0.0 b.isNaN // true |
異型間の代入
Swiftでは型が異なる変数同士の代入を暗黙的にできない。イニシャライザをかます。
縮小方向の代入は端数処理が行われる。
1 2 3 4 5 |
let a: Int32 = 100 let b: Int64 = a // Cannot convert value of type 'Int32' to specified type 'Int64' let c: Int64 = Int64(a) let x: Float = 1.0 let y: Int = Int(x) // 1 |
異型間の比較
Swiftでは型が異なる変数同士の比較ができない。イニシャライザをかます。
結構、型の違いにシビアなのか…。
Int32とInt64は比較できるみたい。FloatとDoubleはできない。
1 2 3 4 |
let a: Float = 100.0 let b: Double = 100.0 let c = (a == b) // Binary operator '==' cannot be applied to operands of type 'Float' and 'Double' let d = (a == Float(b)) // true |
String型,リテラル
文字列リテラルはダブルクォーテーション。エスケープは一般的なやつが使える。
リテラル内での変数展開は\()演算子。${}の方がいいのに…。
1 2 |
let x: Int = 100 let v: String = "hoge\(x)fuga" // hoge100fuga |
kotlinでも見かけたスリーダブルクォート。これの発祥はPythonらしい。
面白いのがスリーダブルクォート内において変数自体のインデント分が無視されること。
kotlinのTrimよりも強烈。以下、sとrの出力は同じになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
let s: String = """ Swift入門 Swift入門 Swift入門 """ print(s) let r: String = """ Swift入門 Swift入門 Swift入門 """ print(r) |
String.Index
StringとCharacterの関係も一般的。String内のCharacterを指す方法が用意されている。
String.startIndex(=0),String.endIndex(=文字数)を使ってCharacterを指す例。
1 2 3 4 |
let a: String = "abcde" let b: Character = a[a.startIndex] // a let lastindex = a.index(a.endIndex,offsetBy: -1) let c: Character = a[lastindex] // e |
Int型とString型の変換
イニシャライザを通す。変換できない場合はnilが入る。
1 2 3 4 |
let i: Int = 100 let s: String = String(i) let s2: String = "hoge" let i2 = Int(s2) // nil |
文字列の結合
文字列の結合は+演算子。append(_:)も使える。
1 2 3 4 |
let s1: String = "hoge" let s2: String = "fuga" let s3: String = s1 + s2 let s4: String = s1.append(s2) |
配列,配列リテラル
配列はArray
配列リテラルは[]。同一型リテラルの配列から型推論可能。
順序数で要素にアクセス可能。
append(_:)で追加、insert(_at:)で挿入、remote(at:)で削除。
1 2 3 4 5 6 7 8 9 10 11 12 |
let ary1: Array<Int> = [1,2,3,4,5] let ary2: [Int] = [1,2,3,4,5] let ary3 = [1,2,3,4,5] // Intの配列 let ary4 = ["hoge","fuga"] // Stringの配列 let a = ary1[0] // 1 ary1.append(6) // [1,2,3,4,5,6] ary1.insert(10,at: 1) // [ 1,10,2,3,4,5,6] ary1.remove(at: 0) // [10,2,3,4,5,6] ary1.removeLast() // [10,2,3,4,5] ary1.removeAll() // [] |
Dictionary型,リテラル
Key->Value辞書はDictionary
辞書リテラルは[“key1″:”value1″,”key2″:”value2”]のように書く。
同一型の辞書から型推論可能。
1 2 3 |
let dic1 : Dictionary<String,Int> = ["a":1,"b":2] let dic2 : [String:Int] = ["a":1,"b":2] let dic3 = ["a":1, "b":2] |
Keyは一意でないといけないので、Keyとして使える型には制限がつく。
DictionaryはDictionary
に準拠したもの(Keyからハッシュ値を計算できる)のみ使える。
DictionaryへのアクセスはOptional(Value)を返す。つまり無いキーの値はnil。
1 2 3 |
let dic3 = ["a":1, "b":2] let a = dic3["a"] // 1 let b = dic3["c"] // nil |
更新,追加は同じ。存在するkeyを指定して値を代入すれば更新、存在しないkeyなら追加。
削除はnilを設定する。
1 2 3 4 |
var dic4 = ["a":1,"b":2] dic4["b"] = 3 // ["a:1","b":3] dic4["c"] = 4 // ["a:1","b":3,"c":4] dic4["a"] = nil // ["b":3,"c":4] |
範囲型
Swiftの範囲型は数学的なモデリングになってる。
1つ目は開区間、半開区間、閉区間。2つ目は順序(計数可能)。
数値以外の範囲についてもforループを回して順番に要素にアクセスできたりする。
範囲型が範囲を定義すると、範囲に収まる値が得られるのではなく、
区間と上限下限をもったオブジェクトのまま保持される。
1 2 3 4 5 6 7 8 9 10 |
let range1 = 1..<4 // CountableRange(1..<4) : lower bound=1,uppper bound=4 for value in range1 { print(value) // 1 2 3 } let range2 = 1...4 // CountableCloseRange(1..4) : lower bound=1,uppper bound=4 for value in range2 { print(value) // 1 2 3 4 } let range3 : Range<Int> = 1..<4 // Range<1..<4) :lower bound=1,upper bound=4 let range4 : Rnage<Int> = 1...4 // Range<1..4) : lower bound=1,upper bound=4 |
Optional型
型がnilを許容するか否か。Wrapped型がnilを許容する場合はOptional
Suger Syntaxとして、Optional
1 2 3 4 5 |
let ival1: Int = 32 let ival2: Int = nil // Nil cannot initialize specified type 'Int' let ival3: Optional<Int> = 32 let ival4: Optional<Int> = nil let ival5: Int? = nil |
ぬるぽ防止(Unwrap)
デフォルトではOptional
明示的なアンラップ(Optional
??演算子を使うと、左辺のOptional(Wrapped)がnilの場合に右辺、
nilでない場合にWrappedをアンラップして返す。
!演算子を使うと強制アンラップ。当然nilならぬるぽ。ぬるぽ時のエラーがドキッとする。
当然、強制アンラップは非推奨。
1 2 3 4 5 6 7 8 9 10 11 12 |
let ival6: Int? = 100 let ival7: Int? = 200 let ival8 = ival6 + ival7 let ival6: Int? = 100 let value1 = ival6 ?? 3 // 100 let ival7: Int? = nil let value2 = ival7 ?? 3 // 3 let ival8: Int? = 100 let ival9: Int? = 200 let ival10 = ival8! + ival9! // error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0). |
ぬるぽ防止(Optional chain)
Optional
?.演算子を使うと、nil、非nilの場合を1行に含めることができる。
左辺がnilである場合はnil、nilでない場合はOptional(Wrapped)を返す。
?.を数珠つなぎに書いていくと、最初のnilで評価が止まってそれ以降のnil参照を行わない。(なのでchain)
1 2 3 4 |
let optionalDouble1 = Optional(1.0) // Optional(1.0) let optionalInfinite1 = optionalDouble1?.isInfinite // Optional(false) let optionalDouble2 :Double? = nil let optionalInfinite2 = optionalDouble2?.isInfinite // nil |
強制アンラップ構文
ぬるぽを恐れるあまり、nil可能なケースが面倒になっているのを和らげるためか、
何か迷いのようなものを感じるけれども、Optional
型としてOptional
当然、nilが入っていればぬるぽで止まる。
1 2 3 4 5 6 |
let intval: Int? = 100 print(intval + 200) // Value of optional type 'Int?' must be unwrapped to a value of type 'Int' let intval: Int! = 100 print(intval + 200) // 300 let intval2: Int! = nil print(intval2 + 200) // error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0). |
アップキャスト
継承関係やプロトコル(インターフェース?)準拠により上位型の存在が確実な場合、
アップキャスト可能。アップキャストはas演算子。
確実な上位関係でない型へのアップキャストは実行時エラー。
アップキャストは暗黙的に実行可能。
1 2 3 4 |
let str : String = "hogehoge" let any : Any = str as Any let int : Int = str as Int // Cannot convert value of type 'String' to type 'Int' in coercion let any2 : Any = str |
ダウンキャスト
上位型から下位型へのキャスト。失敗のリスクがある。
失敗のリスクをOptional
as?によるダウンキャストはOptional
as!が失敗した場合、実行時エラーが発生する。
1 2 3 4 |
let any = 1 as Any let int = any as? Int // Optional(1) let any2 = 1 as Any let int2 = any2 as! Int // 1 |