あるアプリ開発で、クロージャがすんなり理解できずに思いっきりハマったので、自分の頭の整理ついでに簡単にまとめてみました。
クロージャとは何か
- クロージャは簡単に言うと、自分を囲むスコープの変数を使うことができる。
- 変数に関数を代入できる。
Wikiや公式で書いているクロージャの説明はハッキリ言って理解しにくいので、いまはこういう認識で良いかと・・・
クロージャの書式
{ (引数1: 型1, 引数2 : 型2, ...複数可) -> 戻り値の型 in
return 戻り値
}
クロージャの書式は上記のようになる。
例えば、String型の引数を何らかの処理をして、String型で返す場合は以下のようになる。
{ (str: String) -> String in
//処理とか
return 戻り値
}
簡単な例
func selfInfo(name: String) -> () -> Void {
func hello() -> Void {
print("私の名前は\(name)です")
}
return hello
}
var myName = selfInfo("山田太郎")
myName() // 私の名前は山田太郎です
selInfoは ()-> Void
という型の関数を返す関数です。
func helloの型は()-> Void
の挨拶をする関数です。(->Voidは省略できます。)
上記の例で分かることは、SelfInfoのnameという引数を、中にあるhello関数が自分の変数として使うことできる点です。
func selfInfo(name: String) -> () -> Void {
var count = 1
func hello() {
print("私の名前は\(name)です。この挨拶は\(count)回目です。")
count += 1
}
return hello
}
var myName = selfInfo("山田太郎")
myName() // 私の名前は山田太郎です。この挨拶は1回目です。
myName() // 私の名前は山田太郎です。この挨拶は2回目です。
myName() // 私の名前は山田太郎です。この挨拶は3回目です。
selfInfo内で宣言されているcountという変数を、中にあるhello関数が値を自分のものとして操作することが出来ていることがわかります。
結局何がいいの?
クロージャの書式はハッキリ言ってわかりにくいです。どういうシチュエーションで使うと使い勝手が良いみたいな解説でよく使われる例がsort()関数とクロージャの組み合わせです。ここで同じようなものを書いても、既にいろいろなところに溢れている情報なので、ちょっと違った解説を書いてみたいと思います。
クロージャの型を省略できる
Swiftのクロージャは型推論が出来ます。
本来、厳密に型まで書いてクロージャを使うと以下のようになります。
let add: (Int, Int) -> Int = {(int1: Int, int2: Int) -> Int in
return int1 + int2
}
let result = add(10, 20) // 30
Swiftでは、このクロージャの型を以下のように省略ができます。
let add = {(int1: Int, int2: Int) -> Int in
return int1 + int2
}
returnを省略できる
let add = {(int1: Int, int2: Int) -> Int in int1 + int2}
処理の長さにもよりますが、一行で書いても問題ない内容なら、上記のようにreturnを省略することもできます。(ただし、複数の返り値がある場合は別)
引数の型も省略できる
let add = {(int1, int2) -> Int in int1 + int2}
##自分が引っかかったポイントとクロージャの恩恵
あるAPIを操作していた時に、値を取得出来ないという事象が発生しました。
理由としては、APIなどの通信は別スレッドとして処理するため、値が返ってくる前にメインスレッドで値をretrunしていて、空のデータが返ってきてしまうというものです。この問題点は別スレッドの"処理が終わってから"値を返すという動作を記述していなかったという点です。
実際はもう少し複雑ですが、以下のようにして、クロージャを使って値を取得することが出来ました。
func request(comp: (response: String) -> Void) {
let url = "http:~~~~"
// 非同期処理
let response = "JSON!!!"
comp(response: response)
}
var responseAns = ""
request { (response) in responseAns = response}
print(responseAns)
SwiftTaskなどを使えば、もっと直感的な記述ができますが、何が起きているか理解する必要もあるので、クロージャの理解は必須だと思います。