Swiftに限らずプログラミングを勉強していると出てくるコールバックですが、
言語によって書き方が若干異なります。
覚えておくと便利なので、しっかりメモしていきます!
Contents
そもそもコールバックって??
コールバックとは、call backです。
つまり「あとで呼ぶ」ということになります。
ある関数の中で別の関数を呼ぶイメージです。
結構シンプルです。
func aaa() { print("aaaです") bbb() } func bbb() { print("bbbです") } aaa() // aaaです // bbbです
こういった関数の中で関数を呼ぶ処理はよく書かれると思います。
これはコールバックではありませんが、非常に近い例です。
コールバックでかく
上の例をコールバックに直してみます。
func aaa(function: (Void) -> Void ) { print("aaaです") function() // ここでbbbが実行される } func bbb() { print("bbbです") } aaa(function: bbb) // aaaです // bbbです
このようになります。
解説していきます!
function: (Void) -> Void
難しいのはこの辺ですよね。
functionは引数名です。正直なんでもOKです!
(Void) -> Voidは引数の型です。(型) ->型 で関数を表現することができます。
Voidは「空っぽ」の意味です。
つまり
「Voidを引数にとりVoidを戻り値にとる関数」== 「引数を取らない戻り値のない関数」
ということになります。
でも、関数bbb内にVoidがかかれてない??
そうなんです、実は上記の関数bbbは省略された書き方なんです。
Voidの意味は空っぽなので、省略できるのも納得ですよね。
関数bbbを省略せずに書くとこのようになります。
func bbb(_: Void) -> Void { print("bbbです") }
これによって「Voidを引数にとりVoidを戻り値にとる関数」である関数bbbを引数にとることができるのです。
じゃあfunction: (Void) -> Voidももっと短くかけるんじゃないか?
このような疑問は出てきますが、コールバック(関数の引数に関数をとる)の場合はVoidを省略することはできないのです。
Trailing Closure
Trailing Closureという記述方法があります。
直訳では「末尾にクロージャ」という意味になります。
これだけでは謎ですね。
百聞は一見に如かずです!とりあえずみてみましょう。
func aaa(function: (Void) -> Void ) { print("aaaです") function() } func bbb(_: Void) -> Void { print("bbbです") } aaa() { () in return bbb() }
今までとは全く違うクールな書き方です。
aaa()の後ろに { } が来て、その中に処理を記述していきます。
今回の例が悪すぎて、
Trailing Closureにするメリットは全くないですがよく見る書き方なのでぜひマスターしたいところです。
特徴としては引数名のfunctionを書かなくても良いところですね。
別の例をあげます。
上の例よりはまともですが、少し難しいかもです。
func plusEnjoy(name: String, callback: (String) -> (String)) { print(callback(name) + "楽しい!!!") } plusEnjoy(name: "たけし") { (i) in return i + "はとても" } // たけしはとても楽しい!!!
引数”たけし”が入ったnameはcallbackの引数になり、Trailing Closureの『i』に送られます。
そしてreturnによってi( == name == “たけし”) に “はとても” が足されたものが返され、
最終的には “楽しい!!!” と合わさってprintされるという流れです。
Trailing Closureの魅力の一つは関数の後ろにある( )内から切り離して記述できるところです。
かっこよく・適切に使っていきましょう!!
コールバックではないけど関数を引数に取る関数
コールバックではないけど関数を引数に取る関数があります。
謎ですね。
こちらです。
func ccc(function: ()) { print("cccです") } func ddd() { print("dddです") } ccc(function: ddd()) // dddです // cccです
関数cccがそれに当たりますね。
function: () の()によって関数を取ることを表現しています。
しかし、cccの中にfunctionの記述はありません。
にも関わらず、cccの引数にdddを格納するとしっかりと実行されます。
不思議ですよね。。。
これについては僕も全く理解できていないので、ご存知の方は教えていただけると嬉しいです。
2018年3月3日編集
こちらについてコメントで教えて頂いたので追記します!ありがとうございます!!
上記で「function: () の()によって関数を取ることを表現しています。」と書いていますが、
この考え方は間違っているようです!
『()は関数を取ることを表現しているわけではなく、()の型エイリアスとしてVoidが定義されているから、cccはVoid型の値を引数に取るというような意味になる。』とのことでした。
わかりやすく省略の無いように書くとこのようになります。
func ccc(function: Void) -> Void { print(“cccです”) } func ddd(Void) -> Void { print(“dddです”) } ccc(function: ddd()) // dddです // cccです // つまり、9行目でやっていることは以下と同義なので、関数が渡されている訳ではない let x: Void = ddd() ccc(function: x)