Posted at

Closureって美味しいの?

More than 1 year has 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()