はじめに
大変お世話になっているこちらの本Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語を読み返す度に「これちゃんと理解してなかった」という内容に出会うので、一度まとめてみました。
ありがたいことに、こちらで本の内容(一部?)が公開してありました。
あとは公式ドキュメントも参考に。
enum
- プロパティ
- コンピューテッドプロパティ
- スタティックプロパティ
が使える
※var point = 100
のようなストアドプロパティは使えない
enum Mahjong {
case pon
case chii
case kan
// コンピューテッドプロパティを定義
var claim: String {
switch self {
case .pon: return "ポン"
case .chii: return "チー"
case .kan: return "カン"
}
}
// スタティックプロパティを定義
static let points = 25000
}
- メソッド
enum
は、メソッドを定義することができる。
また、mutating
メソッドを使うことでenum
自身を変更することができる。
enum Mahjong {
case pon
case chii
case kan
// メソッドを定義
func claim() -> String {
switch self {
case .pon: return "ポン"
case .chii: return "チー"
case .kan: return "カン"
}
// mutating で変更可能
mutating func update(mahjong: Mahjong) {
self = mahjong
}
}
- デフォルト値
enum
に型を指定するとrawValue
でデフォルト値が取得できる
enum Wind: Int {
case east // Wind.east.rawValue: 0
case south // Wind.south.rawValue: 1
case west // Wind.west.rawValue: 2
case north // Wind.north.rawValue: 3
}
enum Mahjong: String {
case pon // Mahjong.pon.rawValue: pon
case chii // Mahjong.pon.rawValue: chii
case kan // Mahjong.pon.rawValue: kan
}
Raw Value
はcase
に値を代入することで定義できる
enum Wind: Int {
case east = 10 // Wind.east.rawValue: 10
case south = 20 // Wind.south.rawValue: 20
case west = 30 // Wind.west.rawValue: 30
case north = 40 // Wind.north.rawValue: 40
}
enum Mahjong: String {
case pon = "ポン" // Mahjong.pon.rawValue: ポン
case chii = "チー" // Mahjong.pon.rawValue: チー
case kan = "カン" // Mahjong.pon.rawValue: カン
}
- allCases
を使う
CaseIterable
プロトコルに準拠させることで、ケースを配列で取得できる
※case
はカンマでつなげて書いてもよい
enum Mahjong: CaseIterable {
case pon, chii, kan
}
// Mahjong.allCases // [pon, chii, kan]
- Associated Values
を使う
Associated Values
を使うとcase
に付属的に関連する値をつけることができる
enum Mahjong {
case score(east: Int, south: Int, west: Int, north: Int)
func data() -> [Int] {
switch self {
case .score(let east, let south, let west, let north):
return [east, south, west, north]
}
}
}
※全ての値がlet
orvar
で一致している場合は
case let .score(east, south, west, north)
と書ける。
Associated Type
Associated Type
を使用すると、1つの型に依存しない抽象的なプロトコルを定義することができ、具体的な型はプロトコルを準拠させるタイミングで指定させることができる。
protocol プロトコル {
associated type 関連型
var プロパティ名: 関連型
func メソッド名(引数: 関連型)
}
プロトコルを準拠させる側では、typealias
を使って、具体的な型を指定する。
struct ProductItem: プロトコル {
// String型で具体的な型を指定
typealias 関連型 = String
var プロパティ名 = "pon"
}
クロージャ
- escaping
escapingをつけることによって、クロージャが関数内部の変数をキャプチャし、関数外のスコープでも変数を保持し実行することができる。
具体的にはクロージャを非同期処理の後に実行したいときなど。
非同期処理を実行する多くの関数は、completionHandler
としてクロージャ引数を取り、実行が完了するまでクロージャーは呼び出されないので、escaping
して後で呼び出す必要がある。
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
escaping
しない場合、クロージャは関数外のスコープで処理を実行できないため、例えばこうなる。
func someFunctionWithoutEscapingClosure(completionHandler: () -> Void) {
completionHandlers()
}
- キャプチャとキャプチャリストとは
クロージャのキャプチャとは、クロージャが定義されたスコープに存在する変数や定数への参照を、クロージャ内のスコープでも保持することをいう。
デフォルトでは、このキャプチャはクラスのインスタンスへの強参照になるため、クロージャが解放されない限りキャプチャされたクラスのインスタンスが解放されない。
このような場合、キャプチャリストを用いることで弱参照を持つことができる。キャプチャを弱参照にすると、クロージャの解放状況に依存せずクラスのインスタンスの解放が行われる。また循環参照の解消にも役立つ。
キャプチャリストを定義するには、クロージャの引数の定義の前に[ ]
を追加し、内部にweak
キーワードもしくはunowned
キーワードと変数名もしくは定数名の組み合わせを列挙する。
{ [ weak or unowned 変数名 or 定数名] (引数) -> 戻り値 in
クロージャの呼び出し時に実行される文
}
※weak
とunowned
の違いはOptional型かどうかで、unowned
を使い変数がnilの場合は実行時エラーが発生する
おわりに
Associated Type
はなんとなくでも使えてもないかも、、、enum
と[weak self]
はよく使っていますが理解はできてなかったようで、まとめたことによりやっと理解できました。
今回の内容は、こちらの続き
でしたが、まだ分かっていないことがありそうなので、継続して読み返していきたいと思います。
参考