Help us understand the problem. What is going on with this article?

Closureって美味しいの?

More than 3 years have passed since last update.

結論から言うと美味しいです。た、正しく理解しないと何が美味しいのか分からなかったので、とりあえず味見してみました。

SwiftのClosureって何?

  • 英語の意味的には、

closure = the process of closing something

何かを閉じる・終わらす一連のイベントのようです。
飲み会の締めの手合わせなども、英語ではクロージャーって呼ぶことができます。
"Let's do the closure for the Nomikai!" とかね。

  • プログラム的には、

名前がない関数。{}で囲まれたブロック。メソッドのように呼び出すのではなく、その場で実行される。関数の引数でよく利用される。(引用 by Merillさん)

基本的な書き方

{ (parameters) -> returnType in
    statements
}

関数 vs クロージャー

基本的なプログラムを書く分には、普通の関数のメソッドを使っても同じことができます。

例1:クロージャーを変数にした場合

// クロージャー
let divide = {(val1: Int, val2: Int) -> Int in
    return val1 / val2
}

//関数
func divide(_ val1:Int, _ val2:Int) -> Int{
    return val1 / val2
}

実行(関数もクロージャーも同じ)

print(divide(val1,val2)) // 2

例2:クロージャーをメソッドの引数として渡す場合

func add(_ closure: (Int, Int) -> Int) {

    // closure(10, 20) = num1 + num
    print(closure(10, 20))
} 

実行: クロージャー(関数では実行分を書くのが難しい)

// クロージャをメソッドの引数として渡す
add({ (num1, num2) -> (Int) in
    return num1 + num2
})

// ↑上の省略バージョン
add({ (num1, num2) in
   num1 + num2
})

クロージャーは色々な書き方ができて、戻り値や、RETURNという単語を省略することができます。分かりにくいので自分は省略しない派です。

例3:shorthand argument names(省略引数名)を使ってクロージャーを作る

func applyMutliplication(_ value: Int, multFunction: (Int) -> Int) -> Int {

    print(count = count + 1)
    return multFunction(value)
}

実行1: 普通のクロージャー

var result = applyMutliplication(2, multFunction: {value in
    value * 3
})
print(value) // 6

実行2:省略引数(\$0,$1...)を使った場合。

var result = applyMutliplication(2, multFunction: {$0 * 3})
print(value) // 6

実行結果は全く一緒です。

ここまで見て、結局クロージャーは美味しいの?って話ですが、
変数をクロージャーにできると、初期化ができたり、実行メソッドを一回書くだけで複数のアクションを起こしてくれたりするのはやっぱり便利だと思う。

ですが、まだ慣れないうちは、普通の関数で書いておいて、慣れたらクロージャーを使って行けばいいよ、という話かなー。って思いました。

もちろんクロージャーを避けては通れないのが、下記に書いたパターンです。

クロージャーが良く使われる例

クロージャーでいちばん使われている例は、completion block(戻り値ブロック構文?) と higher order functions(高階関数)の二つです。

Completion blocksってなに?

例えば、時間のかかるタスクがあったとして、処理が終了した時にお知らせしてほしいですよね?クロージャを使うと、delegateのデザインパターンを使わなくても済みます。

例1:配列を回す処理

func bigTask(completion: () -> ()) {

    for index in 0...100 {
        print(index)
    }
    completion() // bigTaskが終わったらお知らせする。
    print("is this done?")
}
//実行メソッド
bigTask { print("finished!") }

実行結果

0
...
998
999
1000
finished!
is this done?

例2:非同期処理

これを使う時は、処理が大きいため、async(非同期)に処理しておいて、終わったらお知らせするというパターンが多い。例えば、URLを使って、画像をダウンロードする場合など。

(URLSession(configuration: URLSessionConfiguration.default)).dataTask(with: imageURL, completionHandler: { (imageData, response, error) in

            if let data = imageData {

                DispatchQueue.main.async {
                    //ダウンロードが終わったら、画像を見せる
                    self.imageView.image = UIImage(data: data)
                }
            }
}).resume()
Saayaman
バイリンガル・デザイナー・フロントエンドです。 記事を英訳してます。Githubリンク載せてます。画像いっぱい載っけてます。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした