Open9

[Swift]プログラミング言語Swiftとは

🍤🍤

swiftの特徴その① モダンな設計

セミコロンの排除

多くのプログラミング言語において、セミコロンさんは文の終端や区切りを意味する。
例えば

print "ほげ";print "ほげ2";

と書くと、コンピュータは2つ文章があると認識してくれる。
これはswiftにおいては存在しないらしい。

型推論

プログラミング言語の機能の1つで、静的な型付けを持つ言語において、変数や関数シグネチャ(メソッドを識別するための情報)の型を明示的に宣言しなくても、初期化のための代入式の右辺値や関数呼び出し時の実引数などといった、周辺情報および文脈などから自動的に(暗黙的に)各々の型を決定する機構のこと。
swiftにおいてはむしろ積極的に使用したほうが良いものらしい。

クロージャ

関数オブジェクトの一種。
波カッコ{}のブロックで囲んだ中にある処理を実行する自己完結型の機能。
簡単に言えば関数のように名前を付ける必要がなく、指定した変数や引数に関数の処理を直接代入するような使い方をする。

 { (引数) -> 戻り値の型 in
    処理
 }

なぜクロージャ(Closure)と言うのか?

ジェネリクス

ジェネリクスとは、プログラミング言語の機能・仕様の一つで、同じプログラムコードで様々なデータ型のデータを処理できるようにするもの。
IntやStringなどの具体的な型の代わりに型パラメータ(Type Parameters)を使い、柔軟に型を指定する。
これちょっと型推論との結びつきを感じる。
型推論は型を明示しなくても、型をつけてくれるやつ
ジェネリクスは、型を定義せずに実際に入力された型で、型が決まるやつ

何気なく使っているArrayやDictionaryにもジェネリクスが取り入れられているらしい。
例えば、下記のようにArrayの中にはIntでもStringでも同じように格納できる。

// 例
let intArray = [2, 4, 6]
let stringArray = ["a", "b", "c"]

めっちゃ便利そう。

[Swift]ジェネリクスについて

ネームスペース(名前空間)

名前空間とは、各要素に一意の異なる名前をつけなければ識別できない範囲のこと。また、名前の集合全体を小さな空間に区切り、それぞれに異なる識別名を与えることで、その空間内では他の空間に含まれる名前の競合・衝突を意識しなくて良いようにしたもの。
例えば、「中央区」という行政区名は全国のいくつかの自治体に存在し、それだけではどこを指すのか分からないが、「東京都中央区」「大阪市中央区」と表記すればそれぞれを識別することができる。この「東京都」や「大阪市」が「中央区」に対する名前空間の役割を果たしている。

名前空間 【namespace】 ネームスペース / NS

静的型付き言語

Rubyは動的。Swiftは静的らしい。
結構理由が実践的な部分に寄っていて、折に触れて読み返す必要がありそうな感じした。
動的型付けと静的型付け

🍤🍤

タプル

var tuple1 = genericTuple(123)

print(tuple1) // (123, 123)
print(type(of: tuple1)) // (Int, Int)
//typeを返してくれるメソッド

この時の(123, 123)はIntという属性を持っている。
このように、包括的に返り値を捉えることをタプルという。

🍤🍤

フレームワーク

framework 「枠組み」「骨組み」「構造」
アプリケーションを開発するとき、その土台として機能させるソフトウェアのこと。「アプリケーションフレームワーク」とも呼ばれる。
フレームワークは目に見えるもの。アーキテクチャは考え方。

アーキテクチャ

コンピュータ システムの論理的構造。
「ソフトウェアアーキテクチャ」=「ソフトウェア要素とそれらの要素の持つ可視的な性質、そしてそれらの間の関係からなるシステムの構造」単なる「構造」ではなく、そこでの構成要素の性質や機能、要素間の関係の意味について重視。
「設計思想」
あんまり深入りしない方が良さそう。
MVVM(Model View ViewModel)は意識していく。
元々は建築の世界で使われていたアーキテクチャ

データベース

データベース(英: database, DB)とは、検索や蓄積が容易にできるよう整理された情報の集まり。 通常はコンピュータによって実現されたものを指す。
データベース

🍤🍤

三項演算子

if文を一行にまとめたもの。
単純なものは一行でもいいが、無理をして長いものをまとめなくともいい。

let isSelected = true

if isSelected {
    print("true")
} else {
    print("false")
}
// ↓同じ
isSelected ? print("true") : print("false")

Swift演算子まとめ

🍤🍤

Nil Coalescing 演算子

オプショナル型をan wrappingするやつ。
ここの”??”は真偽判定とほぼ同じ。
nilがある場合は実行される。
Optional Chainingと異なり、この時実行された処理はオプショナル型にはならない。
下記のコードの場合は、Int型になる。

let alpha: Int? = nil
let beta: Int = 10

alpha != nil ? alpha! : beta
// ↓同じ
alpha ?? beta
🍤🍤

Swiftの列挙型(enum)おさらい

どこよりも分かりやすいSwiftの"?"と"!"

・"?"も"!"もオプショナル型であるが、"!"は特に暗黙的アンラップ型(Implicitly Unwrapped Optional)と呼ばれるオプショナル型。

・"?"のオプショナル型の変数は出力したときにOptionalという記述が含まれるようになる。

・「値」と「Optional(値)」は異なる。

Optionalは値を包み込むラップ(包み紙)のイメージです。オプショナル型は値をOptionalというラップ1枚で包み込んでいます。すると、たとえ中身がない(=nil)状態でも包み紙だけは存在するため、とりあえず扱うことができます。
しかし、この便利な包み紙があるために値はラッピングされ、直接扱うことができません。扱うためにはラップを取り除き、中身の値を取り出さなくてはいけません。
この包み紙を取り除き、値を取り出すことをアンラップと言います。

アンラップの方法はいくつかあります。代表的なアンラップの方法は以下です。
Forced Unwrapping (強制的アンラップ)
Optional Binding (オプショナルバインディング)
Optional Chaining (オプショナルチェイニング)
よくわからない横文字の連続でツライですね...
ですが、これらの名前を覚える必要は全くありません。
すべて、目的はオプショナル型の変数から値を取り出すことです。それぞれ使うタイミングと記法が異なる>だけなので、それをキチンと押さえることができればアンラップは怖くありません。

Forced Unwrapping

・Forced Unwrapping(強制的アンラップ)はオプショナル型を強制的にアンラップする方法。強制的アンラップの記述方法は簡単。オプショナル型の変数の後ろに"!"をつける。

//強制的アンラップ
var optional: Int? = 10 // オプショナル型
// そのまま出力
println(optional)
// => Optional(10)

// 強制的アンラップ("!"をつけて)で出力
println(optional!)
// => 10

var a: Int  = 10 // 非オプショナル型
var b: Int? = 10 // オプショナル型
a + b!
# => 20

便利に見える強制的アンラップだが、アンラップする対象のオプショナル型の変数の中身がnilだった場合、エラーになり、アプリケーションが落ちてしまう。

var hoge: Int?
hoge!
# => オプショナル型の変数hogeはnilなのでアンラップするとエラーする

このアンラップに用いる"!"はオプショナル型の変数宣言の"!"とは別物

Optional Binding

オプショナルバインディングを使うと、オプショナル型の変数に値が入っていればアンラップして処理を行い、値がない(=nil)のときには処理を行わない、または違う処理を行うことができる。
オプショナルバインディングはif文などの条件式と組み合わせて使う。
この条件式はアンラップする対象のオプショナル型の変数がnilかどうかを判定するためのもの。

var hobby: String? = "プログラミング"

//unwrappedHobbyに"プログラミング"が代入される
if let unwrappedHobby = hobby {
// hobbyがnilではないため、ここの処理が呼ばれる
    println(unwrappedHobby)
    // => "プログラミング"
}
else {
    println("趣味はありません")
}

Optional Chaining

オプショナルチェイニングもオプショナル型の変数の中身がnilのときに安全にプログラムを実行するための機能。オプショナル型の変数に続けてプロパティを取得したり、メソッドを呼び出す場合に使用。

変数に値が入っている場合、オプショナルチェイニングは変数をアンラップし、中身を取り出す。
オプショナルチェイニングに続けて取得したプロパティやメソッドの戻り値はオプショナル型になる点に注意すること。

var human: Human? = Human()

// nilでないためプロパティが取得できる
human?.name
// => "Optional(シンボ)"

// nilでないためメソッドを呼び出せる
human?.hello()
// => "Optional(こんにちは)"

オプショナル型の変数の中身がnilの場合、オプショナルチェイニングはnilを返します。そしてnilを返した後、それに続く処理をすべてキャンセルする。

var human: Human?

// humanがnilのため、nilが返る
human?.name
// => nil

// humanがnilのため、nilが返る
human?.hello()
// => nil

オプショナルチェイニングを使って取得した値はすべてオプショナル型となるのは、オプショナル型の変数がnilのときは返る値がnilになるため。
nilを扱うことができるのがオプショナル型だけなので、nilが返る可能性がある以上、値はオプショナル型にしなくてはいけない。

##暗黙的アンラップ型"!"
"!"は強制的アンラップの"!"とは別物で、"?"同様オプショナル型であることを示すもの。

// 暗黙的アンラップ型
var unwrappedInt: Int!

// 強制的アンラップ
10 + wrappedInt!

使用するとき必ず強制的アンラップをする点がもう一つのオプショナル型である"?"との違い。
オプショナル型"?"の値は、そのまま使うとOptional()によってラッピングされるので通常の値と併用して使うことができない。
強制的アンラップと同様に変数の値がnilの場合はエラーしてアプリケーションが落ちてしまうので注意が必要。

🍤🍤

Identifiableプロトコル

データを識別可能にするために用意されているもの。
このプロトコルに準拠したクラスやStructは一意に識別できる事を保証される。
プロトコルの要件として、識別子であるプロパティ(id)を必ず有する必要がある。
idは手動で設定もできるが、ユニークなIDを生成するUUID()を利用して自動で設定するのが良い。

 
struct Animal: Identifiable {
    var id = UUID()     // ユニークなIDを自動で設定
    var name : String
}

idを手動で設定する場合は、各要素が必ず一意になるように注意。

プロトコル

プロトコルの宣言時に宣言した関数は、当該プロトコルのインターフェースになる。
クラス、構造体、列挙型などで使われ、プロトコルのインターフェースを満たす型は、プロトコルに準拠していると言われる。
任意のプロトコルに準拠したクラスや構造体(struct)はそのインターフェースがあることが保証される。
型のインターフェースを定義するもの。
クラスの挙動を決めた設計図的なもの。

extension ではこのインターフェースのデフォルト実装があるので、もし準拠した class か struct にこのインターフェースの実装を書いてなければ、extension で定義した実装を流用する。

🍤🍤

イニシャライザ

  • 型(クラス、構造体、列挙体)のインスタンスを初期化(initialize)する特殊なメソッドのこと。
  • funcキーワードが不要。また呼び出し時のメソッド名も省略できる。
  • 全てのプロパティの初期化が完了していないとインスタンスメソッドは実行できない。
init() {
    // 初期化
} //基本の形
class User {
    let name: String

    // funcが不要(initキーワードのみ)
    init(name: String) {
        // 全てのプロパティを初期化する前にインスタンスメソッドを実行することはできない
        // printName() → コンパイルエラー
        self.name = name
        printName() // OK
    }

    // インスタンスメソッド
    func printName() {
        print(name)
    }
}
let user1 = User.init(name: "hoge")
// 呼び出し時のメソッド名が省略可能
let user2 = User(name: "hoge")

なぜ必要か

  • 型の整合性が取れない
  • メモリ安全でない(メモリ確保、初期化がされる前にインスタンスにアクセスしてしまう)