前回に続いて、Swiftの学習日記です。
Swiftのプロトコルについて理解を深めるために、「詳解Swift 第3版」という本を買ってみました。
SwiftでiOSアプリを作成する場合、プロトコルを利用したデリゲートパターンに慣れておく必要があります。
本屋で「詳解Swift 第3版」を立ち読みしたら、Swiftのプロトコルについてガッツリ説明してあったので、読んでみることにしました。
(その他の項目についても詳細に解説してあるので、Swiftの理解を深めるのに役立ちます。)
今さらながら、Swiftには様々な機能が用意されていて、とても便利な言語であると再認識しました。
(まあ、全部の構文を使うことはないと思いますが、知っておいて損はないかも!?)
●目次
(参考)出版社の商品紹介ページ
CHAPTER01 Swiftでプログラミング
CHAPTER02 関数
CHAPTER03 構造体
CHAPTER04 オプショナル
CHAPTER05 基本的なデータ型
CHAPTER06 パターン
CHAPTER07 演算子
CHAPTER08 クラスと継承
CHAPTER09 メモリ管理
CHAPTER10 プロトコル
CHAPTER11 拡張
CHAPTER12 エラー処理
CHAPTER13 クロージャ
CHAPTER14 ジェネリクス
CHAPTER15 C/Objective-Cとのデータ受け渡し
CHAPTER16 Objective-Cとの連携
CHAPTER17 コーディングとデバッグ
APPENDIX A.Swiftの標準ライブラリ
B.構文図
目次の詳細は、以下のようになっていました。
CHAPTER01 Swiftでプログラミング
1.1 データ型と変数
Swiftのデータ型
整数と実数を表す型
論理型、文字型と文字列型
変数と定数
変数の定義
定数の定義
変数と型
文字列
print関数
配列
配列要素へのアクセス
演算子
演算子の記述に関する注意
識別子
型に別名を付ける
型パラメータ
モジュールのインポート
名前空間
コメント1.2 制御構文
文
if文
while文
repeat-while文
for-in文
switch文
ラベル付きのループ文
ラベル付きのif文、switch文
do文1.3 簡単な実行方法
プレイグラウンド
Swift処理系をコマンドラインから使う
SDKの設定
Linuxでの設定
コマンドラインから対話的に利用する
ソースプログラムをコンパイルする
ソースコードを解釈実行する
複数のソースファイルからなるプログラムをコンパイルする
CHAPTER02 関数2.1 関数定義の基本
関数定義の概要
引数ラベル
引数ラベルの指定と省略
仮引数の省略
下線の特殊な用法2.2 関数定義におけるさまざまな設定
inout引数
関数の引数に規定値を指定する
引数の値は関数内で変更できない
返り値を使わない場合を許す指定
関数内の関数2.3 オーバーロード
オーバーロードとは
引数ラベルを使ったオーバーロードの定義
関数の書き表し方2.4 タプル
タプルの概要
タプルと代入操作
タプルを返す関数
キーワード付きのタプル
キーワード付きのタプルと代入
キーワード付きのタプルを返す関数
タプルの比較
CHAPTER03 構造体3.1 構造体の定義
定義の概要
構造体の初期値
全項目イニシャライザ
構造体を定数に代入した場合
構造体の定数プロパティ
イニシャライザの定義
複数個のイニシャライザを定義する
他の構造体を含む構造体
ネスト型3.2 メソッド
インスタンスに対するメソッドの定義
構造体の内容を変更するメソッド
タイプメソッド
イニシャライザとメソッド3.3 プロパティ
プロパティの種類
タイププロパティ
格納型プロパティの初期値を式で設定する
計算型プロパティ
関数のinput引数にプロパティを渡す
計算型プロパティに対する特殊な設定
グローバルなスコープを持つ計算型プロパティの定義
プロパティオブザーバ3.4 添字付け
添字付けとは
添字付けのバリエーション
CHAPTER04 オプショナル4.1 オプショナル型
オプショナル型とnil
オプショナル型の値を開示する
条件判定とオプショナル型4.2 オプショナル束縛構文
if-let構文
オプショナル束縛と条件式
guard文
nil合体演算子
readLine関数4.3 有値オプショナル型
有値オプショナル型とは
オプショナル型を要素とする配列
オプショナル型と関数
有値オプショナル型と関数
関数のinput引数に指定する4.4 失敗のあるイニシャライザ
イニシャライザで正しく処理ができない場合
失敗のあるイニシャライザの定義
CHAPTER05 基本的なデータ型5.1 整数と実数
数値を表すデータ型
数値型のリテラル
数値型のイニシャライザ
数値型のプロパティ5.2 範囲型とStride型
範囲演算子に関係する型の概要
範囲型のインスタンスの生成
範囲型のプロパティとメソッド
Stride型の概要
Stride型とプロトコルStrideable
for-in文とプロトコルSequence5.3 配列
コレクションの内容を変更する
配列の部分的な置換
部分配列の型
配列のイニシャライザ
配列のプロパティ
配列のメソッド
配列の比較
可変個の引数が利用できる関数
多次元配列5.4 文字列と文字
String型のイニシャライザ
Character型
UnicodeScalar型
合成された文字とCharacter型
文字データの取り出し方
文字列の長さ
指定位置の文字および部分文字列の取り出し方
String.Index型の意義
String型のメソッド
複雑な文字列埋め込み
識別子にUnicodeを使う場合の問題5.5 辞書
辞書の型宣言と初期値
辞書へのアクセス
辞書の比較
辞書とfor-in文
辞書のメソッドとプロパティ
CHAPTER06 パターン6.1 タプルとswitch文
タプルをcaseで使う
switch文でwhere節で使う
オプショナル型をswitch文で使う6.2 列挙型
シンプルな列挙型
メソッドの定義
値型の列挙型
列挙型のイニシャライザとタイプメソッド
列挙型のタイプメソッド6.3 共用型の列挙型
共用型の列挙型の概要
if-case文
for-in文でcaseパターンを使う
プロトコルの採用
列挙型の付加情報を書き換えるメソッド
再帰的な列挙型
オプショナル型と列挙型6.4 パターンマッチ
パターンマッチとは
パターンマッチのルール
CHAPTER07 演算子7.1 Swiftの演算子
演算子の優先度
演算子の結合規則
剰余演算子
ビット演算子と論理演算子
オーバーフロー演算子7.2 演算子の定義
演算子の宣言
二項演算子の定義
単項演算子の定義
引数の変数の値を変更するには
パターンマッチ演算子
短絡評価をする演算子の定義7.3 優先度グループ
優先度グループの定義
優先度の指定について7.4 プロトコルと演算子の定義
演算子の宣言を含むプロトコル
演算子を含むプロトコルの採用
CHAPTER08 クラスと継承8.1 クラス定義
クラスの概要
クラスの継承
継承によるクラス定義の例
動的結合とキャスト
クラスメソッドとクラスプロパティ
継承とメソッド呼び出し8.2 イニシャライザ
指定イニシャライザと簡易イニシャライザ
初期化の手順と守るべきルール
クラス継承の例題
初期化は結局どう記述すればよいのか
イニシャライザの継承
必須イニシャライザ
失敗のあるイニシャライザと継承
失敗のあるイニシャライザを失敗しないようにする8.3 継承とサブクラスの定義
プロパティの継承
添字付きの継承
継承とプロパティオブザーバ
継承をさせない指定
キャスト演算子とパターンマッチ
継承と型推論
Objective-Cのクラスを継承
クラスのメタタイプ
継承とSelf8.4 解放時処理
解放時処理とデイニシャライザ
デイニシャライザの定義
デイニシャライザを使った例8.5 遅延格納型プロパティ
遅延格納型プロパティとは
ファイルの属性を遅延評価する例題
CHAPTER09 メモリ管理9.1 参照型データとARC
リファレンスカウンタの概念
ARCによるメモリ管理
値型データの共有とコピー9.2 強い参照の循環
インスタンスが解放できない場合
弱い参照
非所有参照
Xcodeによる参照関係の表示9.3 オプショナルチェーン
オプショナル型の値を連続利用する場合
オプショナルチェーンとは
オプショナルチェーンでメソッドを呼び出す
オプショナルチェーンの型
値を返さないメソッドの場合
オプショナルチェーンに対する代入
CHAPTER10 プロトコル10.1 プロトコルの宣言
プロトコルの概要
メソッドの宣言と定義
プロパティの宣言と定義
イニシャライザの宣言と定義
クラス定義のためのプロトコル10.2 プロトコルと型
型としてのプロトコル
プロトコルの継承
プロトコルの合成
プロトコルへの適合をチェックする
Objective-Cのプロトコルを利用する
オプション項目のあるプロトコルを宣言する
プロトコルの使用例10.3 プロトコルと付属型
ネスト型とプロトコル
付属型が適合するプロトコルを指定
付属型に既定の型を指定する
付属型定義におけるassociatedtypeとtypealias
プロトコル定義におけるSelf
CHAPTER11 拡張11.1 拡張の宣言
拡張の概要
システムの既存の型に対する拡張定義の例
拡張定義とイニシャライザ
拡張定義と継承に関する注意11.2 拡張定義とプロトコルへの適合
プロトコルへの適合
プロトコルに適合するための定義がすでに存在する場合11.3 プロトコル拡張
プロトコルの拡張定義とは
プロトコル拡張に記述できること
プロトコル拡張に型による制約をつける
プロトコル拡張による多重継承11.4 集合とプロトコル
ハッシュ値による検索の高速化
集合の型宣言と初期値
要素へのアクセス
集合演算
プロトコルOptionSet
CHAPTER12 エラー処理12.1 エラー処理構文
エラーへの対応
エラーの発生
エラーの伝播と捕捉
エラーを投げる関数の呼び出し方
do-catch構文
try?とtry!
エラーを投げるオプショナル型の関数とtry?12.2 処理の中断と後始末
defer文の概要
defer文の動作
defer文とARCの動作12.3 アクセス制御
アクセス制御の5つの種類
アクセス制御の指定
クラス継承とアクセス制御
プロトコルとアクセス制御
拡張定義のアクセス制御について
プロパティのアクセス制御12.4 アサーションとテスト
assert
アサーションのための関数
関数assertの引数とデバッグ用リテラル
戻ってこない関数
ユニットテスト
UIテスト
関数debugPrint12.5 利用可能条件とコンパイラ制御文
利用可能条件を指定する属性
利用可能条件に合致する実装を用意する
条件付きコンパイル
CHAPTER13 クロージャ13.1 クロージャの宣言
クロージャの概要
クロージャの仮引数と返り値の型宣言
クロージャと関数の型
クロージャの複雑な型宣言13.2 変数のキャプチャ
キャプチャとは
クロージャが個別にローカル変数をキャプチャする場合
複数のクロージャが同じローカル変数をキャプチャする場合
クロージャが参照型の変数をキャプチャする場合
ネスト関数とクロージャ
メソッドとイニシャライザ
オーバーロード定義された関数を区別する
関数とクロージャ
キャプチャリスト13.3 クロージャの使い方と記法
引数リストの省略
引数リスト自体の省略
配列の整列
配列要素の選択
接尾クロージャ
エラーを投げるクロージャ
再通報する関数
配列要素に対する操作
インスタンス列を使ったプログラミングスタイル13.4 クロージャと強い参照の循環
キャプチャによる強い参照
キャプチャリストによる解決
非所有参照による解決
関数の引数にクロージャを渡す場合
@autoclosureについて
CHAPTER14 ジェネリクス14.1 ジェネリクスの概要
ジェネリクスとは
ジェネリック関数の例
ジェネリックな型の例
ジェネリクスとプロトコル14.2 ジェネリック関数の定義
ジェネリック関数の定義
型パラメータの書き方
プロトコルによる制約のあるジェネリック関数
配列とシークエンスを扱う関数
型パラメータの推論14.3 ジェネリクスによる型定義
型パラメータを持つ構造体の定義
ジェネリックなデータ型と型パラメータの推論
拡張定義の条件に型パラメータを使う
プロトコルを採用した型自体に対する制約を記述する
ジェネリックなクラスを定義する
型パラメータを持つtypealias
プロトコルSequenceを調べよう
プロトコルCollectionを見てみよう
CHAPTER15 C/Objective-Cとのデータ受け渡し15.1 互換なデータ型をポインタ
C言語の単純型とSwiftの型
データのバイト数を調べる
アンセーフポインタ
Null許容性
変数を用意して関数の結果を書き込む
配列を用意して関数の結果を書き込む
C関数のポインタ引数に関するルール
ポインタを使ってメモリにアクセスする
返されたポインタから値を取り出す
コマンドラインの引数リストにアクセスする
T**型の引数の扱い15.2 Data型とポインタ
Data型の概要
Data型でファイルを読み書きする
Data型でバイト列を操作する
UnsafeBufferPointer<T>を利用する
メモリの動的な獲得
アンセーフポインタでDataのバイト列を操作するには15.3 対応づけられたデータ型
数値と数値オブジェクト
FoundationフレームワークとSwiftの型定義
可変なインスタンスと不変なインスタンス
オブジェクト
文字列
配列
集合
辞書
NSValueとCoreGraphicsの構造体
Core Foundation15.4 列挙型とその他のデータ型
列挙型
マクロNS_ENUMで定義された列挙型
マクロNS_OPTIONSで定義された列挙型
定数の集まりを取り込む
マクロ定義
共用体とビットフィールド
関数ポインタ
C言語のブロックオブジェクト
CHAPTER16 Objective-Cとの連携16.1 SwiftとObjective-Cをビルドする
Xcodeの新プロジェクトをSwiftで作る
Objective-Cの情報をSwiftから参照する
Objective-CのヘッダをSwiftのインターフェースとして見る方法
Swiftの情報をObjective-Cから参照する16.2 Objective-Cのオブジェクトを使う
Objective-CをSwiftのメソッドとイニシャライザ
Objective-CのクラスをSwiftから使う例
Objective-Cのプロパティに関する注意
Objective-Cのクラスを継承する
コマンドラインからコンパイルするには
Swiftに見せる情報を変更する
エラー情報を返すメソッド
セレクタを使ったメソッド呼び出し
セレクタを使ったメソッド呼び出しから返り値を得る16.3 SwiftのクラスをObjective-Cで使えるようにする
Swiftのクラスと@objc属性
Objective-Cに見せる名前を指定する
SwiftとObjective-Cのクラスを互いに使う例16.4 GUI部品との連携
ターゲット&アクション・パラダイム
Cocoaバインディング
CHAPTER17 コーディングとデバッグ17.1 Playground向けの言語仕様
プラットフォームに依存しない機能
色を簡便に表現する方法とその記法
画像を簡便に表現する方法とその記法
リソースを簡便に表現する方法17.2 文書化コメント
Xcode向けのコメント
文書化コメント17.3 デバッガLLDBの簡単な使い方
LLDBとは
REPLモードでの準備
コンパイルしたコードのデバッグ
コマンドのヘルプ
ブレークポイント
コマンドに別名を付ける
変数の値を表示する
ステップ実行17.4 プロトコル指向とは
クラスに基づくプログラミングの問題点
プロトコル指向の概要
プロトコル指向の実践
オブジェクト指向とプロトコル指向
APPENDIX A.Swiftの標準ライブラリA.1 プロトコル
A.1.1 数に関するプロトコル
A.1.2 整数型に関するプロトコル
A.1.3 実数型に関するプロトコル
A.1.4 ExpressibleBy~のグループ
A.1.5 比較とハッシュ
A.1.6 Strideable
A.1.7 シークエンスとイテレータ
A.1.8 コレクション
A.1.9 遅延評価を行うシークエンスとコレクション
A.1.10 実体を持つ型
A.1.11 集合演算のプロトコル
A.1.12 文字列への変換と出力
A.1.13 任意のオブジェクトを表すプロトコル
A.1.14 エラーを表すプロトコル
A.1.15 CVarArg:Cの可変長引数A.2 型
A.2.1 VoidとNever
A.2.2 Bool
A.2.3 整数
A.2.4 実数
A.2.5 文字列
A.2.6 文字を表す型
A.2.7 その他の型A.3 ジェネリック型
A.3.1 Array<T>
A.3.2 Dictionary<Key, Value>
A.3.3 Set<T>
A.3.4 Optional<T>
A.3.5 範囲型
A.3.6 Stride型
A.3.7 その他の型A.4 ポインタ型
A.4.1 UnsafePointer<T>とUnsafeMutablePointer<T>
A.4.2 UnsafeRawPointerとUnsafeMutableRawPointer
A.4.3 UnsafeBufferPointer<T>とUnsafeMutableBufferPointer<T>
A.4.4 UnsafeRawBufferPointerとUnsafeMutableRawBufferPointer
A.4.5 ポインタに関連するその他の型A.5 関数
A.5.1 算術演算
A.5.2 入出力
A.5.3 デバッグ
A.5.4 メモリ
A.5.5 ポインタと関数
A.5.6 インスタンスの生成
A.5.7 その他A.6 属性
A.6.1 @available
A.6.2 @discardableResult
A.6.3 クロージャに関連する属性
A.6.4 Objective-Cとの関連を表す属性
A.6.5 Interface Builderの属性
A.6.6 @convention
A.6.7 @testable
A.6.8 @NSCopy
A.6.9 アプリケーションに関する属性
A.6.10 その他の属性
APPENDIX B.構文図B.1 識別子で使える文字
[1] 識別子の先頭に使える文字
[2] 識別子に使える文字
[3] 演算子の先頭に使える文字
[4] 演算子の2文字目以降に使える文字B.2 構文図
INDEX
●著者紹介
(参考)出版社の商品紹介ページ
荻原剛志(おぎはらたけし)
大阪大学大学院基礎工学研究科修了。工学博士。
大阪大学情報処理教育センターにて、NeXTコンピュータによる教育用計算機システムの導入に携わる。
その後、奈良先端大、神戸大学、高知工科大学、大阪大学を経て2008年4月より京都産業大学コンピュータ理工学部教授。
ソフトウェア開発手法に関する研究、深層暗号に関する研究を行う。
ToyViewer、Typistなど、国際的に定評のある OS X用ソフトウェアの開発者でもある。
著書に「詳解 Objective-C 2.0 第3版」(SBクリエイティブ刊)など。
信州生まれである。
→NeXT(macOSの前身)の時代から使っているなら「筋金入り」ですね!!!
プロフィール
長野県上田高等学校卒業。山梨大学工学部計算機科学科、大阪大学大学院基礎工学研究科を修了。大阪大学情報処理教育センターにてNeXTコンピュータシステムの導入に携わる。奈良先端大、神戸大学、高知工科大学、大阪大学を経て、2008年より現職。
大学の研究室では好きなことを自由にやらせてもらいました。大学の教員を目指した理由のひとつでもあります。
そういわれてみると、本書もアカデミックなかんじ=教科書っぽい、キッチリした本だと思いました。
●Swiftのプロトコル
Swiftの「プロトコル」って、他のOOP言語でいうところの「インターフェース」に該当する機能ですが、呼称の由来について解説がありました。
(p.100) Column プロトコルとインターフェース
Swiftのプロトコルという概念はObjective-Cのプロトコルとほぼ同一です。この概念はJavaなどの他の言語ではインタフェース(interface)と呼ばれています。
プロトコルはもともと、Objective-Cでプロセス、あるいはスレッド間通信を行う際、通信相手となるオブジェクトがどのようなメソッドを実装しているかを効率よく把握するためのものでした。これが抽象型を表現する手段として適していることがわかり、後発の言語であるJavaなどにも取り入れられることになったのです。
従って、プロトコルという名前が元祖で、Swiftがこれをインタフェースと呼ばないのはむしろ当然といえるでしょう。
Swiftの言語仕様で、オブジェクト指向の機能について、呼び方とか用語が他の言語とちょっと違うよな~(紛らわしい?)とか思ってましたが、Objective-C由来なので、インターフェースじゃなくてプロトコルという呼び名の方が先であり、自然なことだったんですね。
→ちょっとした豆知識も書いてあり、面白い本です。
●プロトコル指向プログラミング
以前、Swiftの言語仕様について検索していたら、「プロトコル指向プログラミング」というプログラミングパラダイムが紹介されていました。
「アスペクト指向プログラミング」みたいに、オブジェクト指向プログラミングの亜種みたいなやつかな?と思ってたけど、本書でもプロトコル指向プログラミングについて紹介されていたので、慣れておこうと思いました。
(p.425) 17.4 プロトコル指向とは
■クラスに基づくプログラミングの問題点
2015年に開催されたApple社の開発者向けイベントWWDCで、「Swiftにおけるプロトコル指向プログラミング」というプレゼンテーションが行われました。その内容は、オブジェクト指向プログラミングに対するSwiftの優位性を主張し、Swiftによるソフトウェア開発の方針がどのようなものでありうるかを示す、大変興味深いものです。
■プロトコル指向の概要
極めて漠然としていますが、クラス継承の代わりにプロトコルとジェネリクスの機能を利用してプログラムを構築することと言えるでしょう。この目標を達成するために、Swiftの言語としての特徴がかなり重要度を持っています。
現状ではプロトコル指向とは、Swiftの言語仕様と言語処理系としての特徴にかなり依存した概念になっており、他の一般のプログラミング言語でもすぐに実践できるというわけではありません。
プロトコル思考がオブジェクト指向に代わる方法論になるのか、あるいは補完的な概念なのか、決定するのはApple社ではなく、Swiftを使う開発者であることには間違いありません。
オブジェクト指向とプロトコル指向の違いについて、分かりやすく説明したスライドがありました。
(p.27)
(p.46)
命令型プログラミング(OOP:オブジェクト指向プログラミング等)と宣言型プログラミング(FP:関数型プログラミング等)の特徴を比較した場合、「副作用」をどう扱うか?という点に違いがあります。
OOPでは、
(A) カプセル化による隠蔽
(B) 継承による差分プログラミング
によって、「副作用」を閉じ込めているのが特徴です。
(FPでは、「モナド」によって副作用を切り出して扱うことができます。)
(参考)「はじめてのLisp関数型プログラミング」という本に、OOPとFPの特徴について、分かりやすい説明が紹介されています。
はじめてのLisp関数型プログラミング――ラムダ計算からリファクタリングまで一気にわかる (Software Design plus)
- 作者: 五味弘,272
- 出版社/メーカー: 技術評論社
- 発売日: 2016/03/18
- メディア: 大型本
- この商品を含むブログを見る
さて、この(B)差分プログラミングには、副作用を扱う上で欠点(面倒くさいこと)もあるのですが、 これを改善しようという試みの一つが「プロトコル指向プログラミング」というわけですね!?
新しい属性(制約)を追加するとき、
・クラスの継承だと、例えば親クラスを変更する等、広範囲に影響が出る(副作用が芋づる式に伝播してバグの源になりやすい)けど、
・プロトコルの追加だと、影響の範囲が狭くできる、
という利点があろうと思われました。
iOSアプリ開発でプロトコルによるデリゲートパターンに慣れ親しむだけでなく、プロトコル指向による差分プログラミングのスタイルも取り入れていけば、スッキリしたコードを書けるのではないかと思います。
(まあ、設計が重要になるような、巨大なプログラムを作る機会はないような気もするけどw)
・デリゲートパターン
・プロトコル指向プログラミング
を活用できるように、Swiftのプロトコルについて理解を深めたいと思います。