対象:
Swift3

クロージャ(Swift)

クロージャとは、簡単に言うと「関数」とそれが定義された「環境」がセットになったものだ。「環境」の最たるものが呼び出し元関数のローカル変数だ。つまり、クロージャは実行時にその呼び出し元関数のスコープ内にあるローカル変数にアクセスできるのだ。

また、クロージャには関数のような名前はない。Swiftでは、引数も戻り値もないクロージャを引数として受け取る関数は以下のように宣言できる。

    // 引数、戻り値なしのクロージャを引数として受け取る
    func invokeClosure(closure: () -> ()) {
        // クロージャを呼び出す
        closure()
    }

このinvokeClosure関数を呼び出すときには、呼び出し元ローカル変数の文字列を表示するだけのクロージャを渡す。この場合、クロージャには引数も戻り値もないので{}で囲まれたprint関数だけである。実際にクロージャが実行されるのはinvokeClosure関数に制御が移ってからだが、そのときに呼び出し元のローカル変数を参照できるのはクロージャであるが故である。

        let message = "呼び出し元のローカル変数"
        // 呼び出したメソッド内でmessage変数にアクセスできる
        invokeClosure { print(message) }

クロージャに引数、または戻り値がある場合、型を明示しなければならない。書き方は関数のそれと同じだが、引数の名前は書かなくて良い。

    // Intの引数、戻り値Intのクロージャを引数として受け取る
    func invokeClosureWithParam(closure: (Int) -> (Int)) {
        // クロージャを呼び出し
        print(closure(100))
    }

上記の関数を以下のように呼び出すと、クロージャが呼び出される。クロージャでは受け取った引数を2倍して返しているので、この場合は200が表示されることになる。

        // クロージャの処理を呼び出し元で書ける
        invokeClosureWithParam { (p1: Int) -> Int in return p1 * 2 }

また、上記のクロージャは省略した書き方ができ、引数は左から$0、$1、$2...で代用できる。以下のクロージャでは、受け取った引数$0を3倍して返しているので、この場合は300が表示される。

        // 省略した書き方のクロージャ
        invokeClosureWithParam { $0 * 3 }

最後にsort関数的なものを書いてみる。sort関数は受け取った配列をソートする関数であるが、配列を昇順にソートするか降順にソートするかという挙動は、クロージャに定義する内容によって呼び出し元で決定できる。

    // sort関数的な関数
    func sortLike(_ array: inout [Int], isOrderedBefore: (Int, Int) -> (Bool)) {
        // 実際には配列のソートはしないが、こんな感じにクロージャを呼んでるはず
        for i in 0..<array.count - 1 {
            print("i: \(i) array[\(i)]: \(array[i]) array[\(i + 1)]: \(array[i + 1]) result: \(isOrderedBefore(array[i], array[i + 1]))")
        }
    }

このsortLike関数では、実際には配列をソートしていないが、ソートをするとすれば隣り合う2つのIntを受け取って、それらの大小関係を判定してBoolを返すクロージャを何度も呼び出しているはずである。

        // sort関数的な関数
        var numbers = [1, 9, 7, 2]
        sortLike(&numbers) { $0 < $1 }

上記のコードの実行結果である。

i: 0 array[0]: 1 array[1]: 9 result: true
i: 1 array[1]: 9 array[2]: 7 result: false
i: 2 array[2]: 7 array[3]: 2 result: false
(2015/02/20)
(2016/10/27 更新) Swift 3.0対応。

新着情報
【iOS Objective-C, Swift Tips】画像の向きを指定して保存する(Swift)
【iOS Objective-C, Swift Tips】UIImagePickerControllerの表示を日本語にする(Swift)
【iOS Objective-C, Swift Tips】ウィンドウの階層構造を3D表示する(Swift)

Copyright(C) 2004-2016 モバイル開発系(K) All rights reserved.
[Home]