Table of Contents
Swiftにはクロージャ(Closure)と呼ばれる仕組みがあります。これを利用すると、メソッド(関数)の引数としてメソッドを渡したり、メソッド自体を変数に代入したりといったことができるため、より柔軟な処理を行えるメソッドを作ることができます。
Objective-Cでブロック構文(Block)と呼ばれていたものとほぼ同じです。
クロージャについてのドキュメント
The Swift Programming Language (Swift 3): Closures
ブロック構文についてのドキュメント
クロージャの書き方
クロージャを定義する基本構文は以下の通りです。
{ (引数1, 引数2, ...) -> 戻り値の型 in 処理 }
引数はメソッドと同様、複数受け取ることができます。戻り値がない場合「戻り値の型」には Void もしくは中身のない括弧 () を書きます。
クロージャの使用例
それでは、具体的な使用例を紹介します。クロージャは様々な省略形が許されている構文であるため、書く人によって見た目が大きく変わったりします。
したがって、様々なクロージャを実際に使っているコードを知ることが、クロージャを理解する上で重要となります。
最も簡単なクロージャ
クロージャを引数に取るメソッドに、引数も戻り値もないシンプルなクロージャを渡す例です。
// クロージャをメソッドの引数として渡す simplestClosure({ () -> () in print("最もシンプルなクロージャ") }) // 戻り値も引数もないクロージャを受け取って実行するメソッド func simplestClosure(_ closure: () -> ()) { print("受け取ったクロージャを実行します") closure() print("受け取ったクロージャを実行しました") }
このサンプルの実行結果です。
受け取ったクロージャをメソッド内で呼び出しています。
クロージャの省略形
今回のように、戻り値がないクロージャの場合、戻り値を表す部分を省略することができます。
// 戻り値部分を省略したクロージャ simplestClosure({ () in print("最もシンプルなクロージャ") })
さらに、引数がない場合は引数部分も省略することができます。
// 戻り値部分を省略したクロージャ simplestClosure({ print("最もシンプルなクロージャ") })
よって、クロージャに引数も戻り値もない場合は、中身だけを書けばいいことになります。ただし、クロージャを受け取るメソッドの引数は、このような省略ができません。
// 受け取る側のメソッドの引数リストはちゃんと書く func simplestClosure(_ closure: () -> ()) { print("受け取ったクロージャを実行します") closure() print("受け取ったクロージャを実行しました") }
引数を受け取るクロージャ
受け取った引数をログに出力するクロージャの例です。
// クロージャをメソッドの引数として渡す printText({ (text: String) in print("受け取った文字列: \(text)") }) // 戻り値なしで、文字列を引数にもつのクロージャを受け取る func printText(_ closure: (String) -> ()) { closure("クロージャで受け取る文字列!!") }
今回もクロージャは戻り値がないので、その部分は省略しています。実行結果は以下のようになります。
引数の型も推論可能な場合は以下のように省略できます。
// 引数の型を省略 printText({ (text) in print("受け取った文字列: \(text)") })
戻り値のあるクロージャ
2つの引数を足して、その結果を返すクロージャです。値を返す場合は、メソッドと同様に return を使います。
// クロージャをメソッドの引数として渡す add({ (num1, num2) -> (Int) in return num1 + num2 }) func add(_ closure: (Int, Int) -> Int) { print(closure(10, 20)) // 結果: 30 }
戻り値も型推論ができる場合、省略することができます。
// 戻り値を省略したクロージャ add({ (num1, num2) in return num1 + num2 })
戻り値のあるクロージャの中身が1行だけの時は return を省略しても値が返されます。
わかりにくくなるためあまりお勧めはしません。
// return を省略したクロージャ add({ (num1, num2) in num1 + num2 })
括弧の省略
実は、引数や戻り値を囲む括弧も省略することができます。
// return を省略したクロージャ add({ num1, num2 in num1 + num2 })
クロージャを変数に代入する
クロージャを変数に入れることで、クロージャの宣言をメソッドを実行する部分と分けたり、複数のメソッドで利用したりできます。
// クロージャをあらかじめ定義 let myClosure = { (num1, num2) -> (Int) in return num1 + num2 } // もし可能な限りの省略を行うとこうなります // let myClosure = { num1, num2 -> Int in // num1 + num2 // } // 変数に格納したクロージャを引数に add(myClosure) func add(_ closure: (Int, Int) -> Int) { print(closure(10, 20)) // 結果: 30 }
クロージャはもともと書き方が特殊な上に省略できる部分が多いため、初めて見ると理解するのが難しいです。実際に使って慣れていきましょう。
ここでは紹介しませんでしたが、Swiftのクロージャにはさらに簡潔に書くためのショートハンド変数(Shorthand Argument Names)や演算子メソッド(Operator Methods)などが用意されています。
個人的には省略しすぎると複数人で開発を行うプロジェクトなどの場合は逆にわかりにくくなるため、ほどほどにすべきかと考えています。
変数キャプチャ や @escaping については別の機会に紹介したいと思います。
ピンバック: Swift クロージャー(closure) – 備忘録