Edited at

Swift実践入門 メモ

初心者です。アウトプットしないと忘れるので、メモ。

クロージャやプロトコルの考えがまだ浅く、何かを作成したりして都度確認し、知識を深めた方が良い。

これを見ただけでは全て理解できない。

わかっていたと思っていたものが、わかっていなかったということがわかってよかった。

簡単なアプリとかを作成したこともあってプロパティなど使ったことがあるものに関してはすごく知識が深まった。


Swift実践入門


1章


言語の特徴: 静的型付き言語

コンパイルなどの実行前に変数や定数の型の情報を決定する言語


Optional型

nilを許容できる

これによって安全性があがる。


ジェネリクス型

安全性と再利用性を両立できる型。


標準ライブラリ と コアライブラリ

標準ライブラリはもともと入っていて何もしなくても呼び出せる。

コアライブラリはimportが必要。

・Foundation

・libdispatch

・XCTest


命名規則

型・プロトコル・モジュール: アッパーキャメルケース

変数・定数・関数: ローワーキャメルケース


2章


定数・変数

(var・let) (変数・定数): 型 で定義する。 型の部分を型アノテーションという。 

変数・定数は使用前に値を代入しなければならない。

var は再代入可能

let は再代入不可


型の確認方法

type(of: 変数・定数)


スコープ

ローカルスコープ: 局所的に定義される。関数や制御構文内でしか使えない。

グローバルスコープ: プログラム全体から参照できる。


Bool型

論理積: let a = false && true // false (&&演算子)

複数の真理値がいずれも真であれば真。何かと何かをの積を答えとする。

false && false = true

論理和: let b = false || true // true (||演算子)

複数の真理値の一つが真であれば真。


数値型

整数型: Int Int8, Int16, Int32, Int64がある。

浮動小数点型: Double,Float Doubleの方が精度が高い。

比較: == , <, > etc...

算術: 1 + 1 = 2 // 加算 減算 除算 乗算 etc...


String型

特殊文字(エスケープシーケンス)

\n: ラインフィード

\r: キャリッジリターン

\": ダブルクウォート

\': シングルクウォート

\: バックスラッシュ

\0: null文字

文字列で値をだす

"(変数)"

複数行の文字列を生成

let a = """

~ 文字列 ~

"""

character型: "a" 単体のこと

String.index型: サブスクリプトを使用して値を取り出す。

比較・結合

let a = "abc"

let b = "abc"

let c: character = "d"

a == b // true

a == c // エラー

a + b // "abcabc"

a.append(c) // "abcd"

型変換

let a = "123" イニシャライザを使う

let b = Int(a) // 123 変換される。

let a = "abc" イニシャライザを使う。

let b = Int(a) // nil 文字列は数値化できない為。


Array型 (配列)

Element: プレースホルダ型

括弧(<>)内にプレースホルダ型を持つ型をジェネリクス型という。

[Element] という シンタックスシュガーが用意されている。

配列

let a = [1, 2, 3] // [String] 型推論される

let array: Int = [] 型を明示する

let array = [[1, 2, 3], [4, 5, 6]] // [[Int]]

要素へのアクセス

var a = [1, 2, 3]

a[0] // 1

更新、追加、結合、削除

a[1] = 4

a // [1, 4, 3]

a.insert(5, at: 3)

a // [1, 4, 3, 5]

a.append(2)

a // [1, 4, 3, 5, 2]

let b = [6, 7]

a + b = [1, 4, 3, 5, 2, 6, 7]

a.remove(at: 2)

a //[1, 4, 5, 2]

a.removeLast()

a // [1, 4, 5]

a.removeAll()

a // []


Dictionary型 辞書

キーと値のペアを持つコレクション。キーを元に値にアクセスできる

let a = ["b": "1", "c": "2"] //[String: Int]型

keyには制限があるがValueには制限がない。

keyはHashbleプロトコルに準拠したもの。

let a = ["b": [1, 2,3]] // [String: [Int]]

arrayと同じように削除・追加等、Keyを使って行う


範囲演算子

..< 演算子 終了の値を含まない

let range = 1..<4 // CountableRange(1..<4)

... 演算子 終了の値を含む

let range = 1...4 // CountableCloseRange(1...4)


Optional型

値があるか空のどちらかを表す型

Wrapped型にはなんでも入られる。

2つのケースがある。

値の不在と値の存在

値が不在の場合は型を明示する必要がある

値が存在する場合は型推論できる

Optional型のアンラップ

・オプショナルバインディング

・??演算子

・強制アンラップ

オプショナルバインディング

if文による値の取り出し

??演算子

値が存在しない場合のデフォルト値を指定するのに、中値演算子の??を使う

let optionalInt = nil

let int = optionalInt ?? 3 // 3

強制アンラップ

!演算子によって強制的に値を取り出す。

値が入っていない時にはエラーになるので注意

(オプショナルチェイニング)

アンラップを伴わずに値のプロパティやメソッドにアクセスできる

オプショナル型の変数の後に?をつける。 Int?, String?

nilを許容できる。

返り値がオプショナル型になる。再度アンラップが必要。

nilが入ってもnilを返す。

そしてそのあとの処理はすべてキャンセルになる。

(暗黙的にアンラップされたオプショナル型)

強制アンラップと同じく値がないとエラーになる

値を明示しなくはならない。

let a: Int! = 1

let b: Int! = nil //エラー


Any型

なんでも許容できる

ゆえに危険。


タプル型

var tupple: (Int, String)

アクセス方法

var tupple: (1, "a")

インデックスによるアクセス tupple.0 // 1

var tupple: (int: 1, string: "a")

要素によるアクセス tupple.int // 1

let int: Int

let string: String

代入によるアクセス (int, string) = (1, "a") // int = 1 string = "a"


Void型 空のタプル

要素の型が0のタプル型をVoid型という。


型キャスト

アップキャスト

上位の型として扱う

中置演算子 as を使って左辺の値を右辺の型の値として使える

let any = "abc" as Any // StringをAnyへ

ダウンキャスト

下位の型として扱う

アップキャストは常に成功するがダウンキャストはコンパイル可能でも失敗する可能性がある。

as?演算子は左辺の値を右辺の型へダウンキャストし失敗した場合はnilを返す。結果としてオプショナル型を返す。

強制キャスト

as!演算子を使う。

失敗したらエラーになる


3章 制御構文

分岐を行うにはif、guard、繰り返しを行うfor,whileがある。

プログラムの制御を移すfallthrogh, break, continueなどがある。

if文 条件の成否による分岐

- else節 条件不成立時の処理

if-let文 値の有無による分岐

if-case文 パターンマッチによる分岐

guard文 条件不成立時に早期退出する分岐

- ifとguard文の使い分け

  guard文は早期退出に適している。

  変数をguard-let文の後でも使える

  ifは{}の中でしか使えない。

switch文 複数のパターンマッチによる分岐

- defaultキーワード デフォルトケースによる網羅性の保証(なんのこっちゃ)

- whereキーワード ケースにマッチする条件の追加

for文 要素の列挙

for-in文 全ての要素を列挙

for-case文 パターンマッチによって絞り込まれた要素の列挙

while文 継続条件による繰り返し

repeat-while文 初回実行が保証されている

fallthrough文 switch文の次のケースへの制御への移動

break文 switch文のケースの実行や繰り返しの中断

continue文 繰り返しの継続

ラベル break文やcontinue文の制御の移動先を指定

return文 

throw文

遅延実行

defer文 スコープ退出時実行される

パターンマッチ

式パターン ~=演算による評価 評価する値がtrueを返す場合にマッチする

バリューバインディングパターン 値の代入を伴う評価

列挙型ケースパターン ケースとの一致の評価

型キャスティングパターン 型キャストによる評価


4章 関数とクロージャ


処理の再利用

関数やクロージャはひとかたまりの処理を切り出し、再利用可能とするためのもの。

再利用できそうな処理を適切に関数・クロージャにすることで、重複した処理を1箇所にまとめ、可読性やメンテナンス性を高めることができる。



  • インアウト引数 関数外に変更を共有する引数(これもう少し調査必要)

    可変長引数 任意の個数の値を受け取る引数


    • 可変長引数を定義するには...を引数の定義の末尾に追加



  • クロージャ(これはもっとコードとか書かないとわからない)

    再利用可能なひとまとまりの処理です。

    簡略引数名 引数名の省略 $に引数のインデックスをつけた$0や$1となる。


クロージャを別のクロージャの引数や関数として利用する場合



  • 属性 クロージャに対して追加情報 @属性名


    • escaping属性 関数に引数として渡されたクロージャが関数のスコープの外で保持される可能性があることを示す属性。 キャプチャが必要。

    • autoclosure属性 引き数をクロージャで包むことによって遅延させる。



  • トレイリングクロージャ クロージャを引数にとる関数の可読性を高めるための仕様。引数のクロージャを()の外に書く記法。



5章

代表的な型を構成する要素

・型が持つ値を保持するプロパティ プロパティは型に紐づいた変数や定数

・型の振る舞いを表すメソッド メソッドは型に紐づいた関数

・初期化を行う イニシャライザ

・コレクションの要素を取得するサブスクリプト

・型内に型を定義するネスト型

インスタンスとは型を実体化したもの。型に定義されているプロパティやメソッドを持つ。

インスタンスそのものでなく、インスタンスのプロパティやメソッドにアクセスする場合

selfを省略できる

インスタンスのプロパティと同名の変数、定数がスコープ内に存在する場合、

selfを明示する必要がある

型をインスタンス化するには、型名に()をつけてイニシャライザを呼び出す。


  • プロパティ

    型の整合性を保つためにインスタンス化の完了までに全てのプロパティに値が代入されていないとだめ。


  • インスタンスプロパティ 型のインスタンスに紐づくプロパティ

     型のインスタンスに紐づくためインスタンスごとに異なる値を持たせることができる

     →一般的にプロパティと呼ぶ


  • スタティックプロパティ 型のインスタンスではなく型自身に紐づくプロパティ

     プロパティの宣言の先頭にstaticをつける。初期値を持たせなくてはならない。

     

    これらはあまり意識したことないが、プロパティの中でも分類がある。

    同じ型を何個かインスタンス化した時に変更するプロパティはインスタンスプロパティ。そうでない変わらないものはスタティックプロパティ。変えたくないときはstaticをつけたほうが可読性が上がる。


  • ストアドプロパティ 値を保持するプロパティ

     値を保持するストアドプロパティ

     値を保持しないコンピューテッドプロパティ


  • プロパティオブザーバ ストアドプロパティの変更を監視


var プロパティ名 = 初期値 {

 willSet {

プロパティの変更前に実行する文

   変更後の値には定数newValueとしてアクセスできる(newValueは暗黙的定数)

}

didSet {

プロパティの変更後に実行する文

}

}


  • レイジーストアドプロパティ アクセス時までに初期化を遅延させるプロパティ
    再代入不可のプロパティには使えない

struct SomeStruct {

var value: Int = {
print(生成)
return 1
}()

lazy var lazyValue: Int = {
   print(生成2)
return 2
}()
}
var someStruct = SomeStruct()
print(インスタンス化)
someStruct.value
someStruct.lazyValue

// 生成
// インスタンス化
// 1
// 生成2
// 2

レイジープロパティはアクセス時に初期化が行われている。



  • コンピューテッドプロパティ 値を保持せずに算出するプロパティ

     プロパティ自体では値を保存せず、すでに存在するストアドプロパティなどから計算して値を返すプロパティ。

     アクセスごとに計算し直すため整合性が保たれる。

     定義するにはvarの後にプロパティ名と型名を指定して{}内にget(ゲッタ プロパティの値を返す処理)とset(セッタ プロパティの値を更新する処理)


    • ゲッタ 値の返却 定義が必要

    • セッタ 値の更新 定義は必要ではない



var プロパティ名: 型名 {

  get {
return文によって値を返す処理
}

  set {
値を更新する処理
     プロパティに代入された値には定数newValueでアクセス可能
}
}


  • イニシャライザ インスタンスの初期化処理

    失敗イニシャライザ 初期化の失敗を考慮したイニシャライザ 

    init? で定義し return nil を返す。 (結果としてoptional型を返す。)


  • メソッド 型に紐づいた関数

    インスタンスメソッド 型のインスタンスに紐づくメソッド 一般的なメソッド

    スタティックメソッド 型自身に紐づくメソッド 


オーバーロード 型が異なる同名のメソッドの定義

引数によるオーバーロード

struct Printer {

func put(_ value: String){
処理
}
func put(_ value: Int){
   処理
}
}
let print = Printer()
print.put = "hoge"
print.put = 123

戻り値によるオーバーロード

struct Printer {

let String = "hoge"
let Int = 123

func put() -> String {
処理
}
func put() -> Int {
   処理
}
}
let print = Printer()
let string: String = print.put()
let int: Int = print.put()
let string = print.put() // エラー


  • サブスクリプト コレクション要素へのアクセス

    配列や辞書などのコレクションの要素へのアクセスを統一的に表す文法


  • エクステンション

    ストアドプロパティを追加することはできないが、コンピューテッドプロパティなら追加できる


型のネスト 型の中に型を定義することができ簡略化できる。

Struct NewsFeedItem {

enum Kind {
case a
case b
case c
}

let id: Int
let title: String
let type: Kind
}

let type = NewsFeedItem.Kind.a
let item = NewsFeedItem(id: 1, title: "Table", type: type)

switch item.type {
case .a : print(a)
case .b : print(b)
case .c : print(c)

// a


6章

型の種類

・構造体 値型

・クラス 参照型

・列挙型 値型


  • 値型 値を表す型インスタンスが値そのものを表す型
    インスタンスを共有することができない。再代入を行わない限り、値は不変。
    他の値の変更による影響を受けない。

mutaiting 自身の値の変更を宣言する

定数に対して再代入不可。


  • 参照型 

    インスタンスを共有できる。インスタンスに対する参照の代入。インスタンスのコピーが発生しない。

    1つのインスタンスが共有されてるため、ある値を変更すると他の変数、定数も伝播する。



  • 構造体 値型のデータ構造

    定数ストアドプロパティは変更不可

    メソッド内のストアドプロパティの変更はmutatingが必要


    • メンバーワイズイニシャライザ デフォルトで用意されているイニシャライザ
      型のインスタンスは初期化後に全てのプロパティが初期化されている必要がある。
      自動でイニシャライズしてくれる。



  • クラス 参照型のデータ構造

    イニシャライズしないとエラーになる

    構造体との違いは参照型・継承が可能。


  • 継承

    新たなクラスを定義するときに他のクラスのメソッド、プロパティ、イニシャライザなどの型を再利用する仕組み。

    差分のみを定義するだけ。

    継承先のクラスは継承元のクラスをスーパークラス。

    継承元のクラスは継承先のクラスをサブクラスと呼ぶ。


  • オーバーライド 型の構成要素の再定義 

    インスタンスプロパティのみ可能


  • finalキーワード 継承とオーバーライドの禁止


  • クラスプロパティ クラス自身に紐づくプロパティ

    インスタンスプロパティとの違いはオーバーライドが可能


  • クラスメソッド クラス自体に紐づくメソッド


  • 指定イニシャライザ 通常のイニシャライザ


  • コンビニエンスイニシャライザ 指定イニシャライザをラップするイニシャライザ

    convenienceキーワードを追加する


  • 2段階初期化(これは今ひとつわからないが初期化しないとダメって覚える)

    型の整合性を保つために3つのルールがある。これらを満たしていないとコンパイルエラーになる。

    ・指定イニシャライザはスーパークラスの指定イニシャライザを呼ぶ

    ・コンビニエンスイニシャライザは同一クラスのイニシャライザを呼ぶ

    ・コンビニエンスイニシャライザは最終的に指定イニシャライザを呼ぶ


  • デフォルトイニシャライザ プロパティの初期化が不要な場合に定義されるイニシャライザ


  • クラスのメモリ管理 ARC(auto reference conuting)

    クラスのインスタンスを生成するたびにそのインスタンスのためにメモリ領域を自動で確保します。

    不要になった時点で解放する。参照がいくつあるかカウントして0になった時メモリが解放される。

    これを参照カウントと呼ぶ。


  • デイニシャライザ インスタンスの終了処理

    スーパークラスのデイニシャライザを呼び出すことはできない



  • 列挙型 複数の識別子をまとめる型


    • ローバリュー
      値をセットすることができる




7章

プロトコルは型が特定の機能や性質をもつために必要なインターフェイスを定義するためのもの。

プロトコルが要求するをンターフェイスを型が満たすことを準拠するという。

プロトコルを利用することで複数の型で共通の性質を抽象化できる

プロトコルのプロパティはvarで宣言する。

→ストアドプロパティかコンピューテッドプロパティかわからない為。

mutatingキーワード 値型のインスタンスの変更を宣言する

値型の場合にfuncの前につける必要がある。

連想型

プロトコルの準拠時にこれらの型を指定する

protocol プロトコル名 {

  associatedtype 連想型名

  var プロパティ名: 連想型名

  func メソッド名(引数名: 連想型名)

  func メソッド名() -> 連想型名

}

連想型名をIntにしたらそれに準拠したものにしなくてはならない。

valueみたいな汎用な型にしてStringならStringに準拠

IntならIntに準拠みたいなこともできる

型制約の追加(これは書いてみないとよく理解できない。)

標準ライブラリのプロトコル

・Equatableプロトコル 同値性を確認するためのプロトコル

・Comparableプロトコル 大小関係を日買うするためのプロトコル

・iteratorProtocolプロトコル 繰り返しのためのプロトコル

・Sequenceプロトコル 要素の列挙のためのプロトコル

・collectionプロトコル サブスクリプトによる要素へのアクセスのためのプロトコル

・Encodable,Decoeable,Codableプロトコル 型をエンコード、デコードに対応させるプロトコル


8章 

ジェネリクス型をパラメータとして受け取ることで汎用的なプログラミングを記述するための機能。

特殊化

ジェネリクスを使用して汎用的に定義されたものに対して、具体的な型引数を与えて型を確定させること。


9章

フレームワーク モジュールやリソースを含むパッケージ

アプリケーション モジュールやリソースを含む実行可能なパッケージ

import モジュール名

アクセスコントロール

・open

・public

・internal // デフォルト

・fileprivate

・private


10章

swiftはできるだけ構造体を利用した方が良い。

コピーオンライト 構造体の不要なコピーを発生させない最適化

クラスを利用すべき時

・参照を共有する必要がある。

・インスタンスライフサイクルに合わせて処理を実行する。

Optional型を利用すべき時

・値の不在が想定される

暗黙的アンラップされたOptional型を利用すべきとき

・初期化時にのみ値が決まっていない。

・サブクラスの初期化より前にスーパークラスを初期化する


11章

イベント通知パターン

・デリゲートパターン

 命名規則 

  - メソッド名はデリゲート元のオブジェクト名から始め、続いてイベントを説明する

  - didやwillなどの助動詞を用いてイベントのタイミングを示す

  - 第1引数にはデリゲート元のオブジェクトを渡す

・クロージャ

 - キャプチャリスト キャプチャ時の参照方法を制御

 - weakキーワード メモリ解放を想定した弱参照

 - unownedキーワード メモリ解放時を想定しない弱参照

 - escaping属性によるselfキーワードの必須化

 - typealiasによる複雑なクロージャの型へのエイリアス

完了イベントだけを受け取れれば良いという時はクロージャを選択すべき

・オブザーバパターン

- selector型 メソッドを参照するための型

1対他のイベントに使う


12章 

非同期処理

スレッドを利用して非同期処理を実現します。

スレッドはCPU利用の仮想的な実行の単位です。

メインスレッドから別のスレッドを作成することもでき、

そこで行われる処理はメインスレッドの処理とは並列に実行されます。

このように複数のスレッドを使用して処理を並列に行うことをマルチスレッド処理という。

マルチスレッド処理は正しく実装しなければ深刻な問題を招くプログラミング手法でもある。

スレッドの過度な使用によりメモリが枯渇したり、

複数スレッド同一データを更新しようとして不整合が発生したり、

スレッドがお互い待ち合いを行うデットロックが発生したり、

注意しなければさまざまな問題が発生したりする。

非同期処理には3つの手法がある。

- GCDを用いる

GrandCentralDispatchキューを通じて非同期処理を行い、直接スレッドを管理することはない。

スレッドの管理はシステムが担当し、CPUのコア数や負担の状況を考慮して自動的に処理を最適化する。

あらかじめ用意されたスレッドを使い回すスレッドの管理手法をスレッドプールという。

直列ディスパッチキュー 現在の処理が終わったら次の処理に進む。

並列ディスパッチキュー 並列で処理を実行する

1つのメインキューと複数のグローバルキューを持つ

優先度のことをQoSという。

・ userInteractive

アニメーションの実行など、ユーザーからの入力に対してインタラクティブに実行され、即時に実行されなければ、フリーズしてるように見える処理に用いる

・ userIntiated

ユーザーインターフェース上の何かをタップした場合など、ユーザーからの実行を受けて実行される処理に用いる

・ default

Qosが何も指定されてない時に利用される。

・ utility

プログレスバー付きのダウンロードなど、視覚的な情報の更新を伴いながらも即時の結果を要求しない処理に用いる

・ background

バックアップなど、目に見えないところで行われて、数分から数時間かかっても問題ない処理に用いる


  • operation,operationQueueクラスを用いる
    タスキのキャンセルができる

  • Treadクラスを用いる
    スレッドの生成と制御をできる。
    スレッドのエントリポイントはmain()メソッドをオーバライドして実装する


13章 14章 15章

以下 省略