[Swift3.0] クロージャの書き方とそれを使ったメソッドのサンプル

By | 2016年10月18日

Swiftにはクロージャ(Closure)と呼ばれる仕組みがあります。これを利用すると、メソッド(関数)の引数としてメソッドを渡したり、メソッド自体を変数に代入したりといったことができるため、より柔軟な処理を行えるメソッドを作ることができます。

Objective-Cでブロック構文(Block)と呼ばれていたものとほぼ同じです。

クロージャについてのドキュメント

The Swift Programming Language (Swift 3): Closures

ブロック構文についてのドキュメント

Getting Started with Blocks

クロージャの書き方

クロージャを定義する基本構文は以下の通りです。

{ (引数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 については別の機会に紹介したいと思います。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です