【Swift入門】クロージャとは|使い方や省略形について

「 よく聞く『クロージャ』って何のこと…? 」

「 関数とは一体何が違うの…? 」

Swift言語における機能の一つ「クロージャ」は、多くのプログラムで活用されており、無くてはならない重要な機能です。

そして、「このコードって何でこうなってるの…?」が発生する要因となりやすい機能でもあります。

ここでは、「クロージャとは何なのか」を始めに、「クロージャが持つ機能」や「使い方」について見ていきます。

本記事を活用して、クロージャに慣れていきましょう!

[ 本記事はこんな人におすすめ ]

・Swift言語でプログラミングの基礎を学習している

・クロージャとは何なのかを知りたい

・クロージャの使い方を知りたい

クロージャってなに?

クロージャ(Closure)とは、Swift言語における機能の一つで、「{ }」で囲まれた何らかの処理を持つブロック(コードの塊)のことを指します。

例えば以下のような、SwiftUIのビュー機能Buttonも、クロージャによってまとまったコードが実行されています。

上記のクロージャを説明するなら、

『このボタンのクロージャは、”ボタンをタップしました”というメッセージをプリント出力する処理を持っている』

と、言うことができます。

機能と特徴

クロージャの主な機能と特徴を、先に挙げておきます。

本記事ではこれらの項目に焦点を置いて、クロージャを解説していきます。

まとまったコード処理を実行できる

定数や変数に代入することが可能

名前を持たない(無名関数)

コードを簡潔にできる(省略形)

はじめのうちは、クロージャについてのイメージがあまりピンと来ないかもしれません(筆者はかなり苦戦しました…)。

このあと少しずつ紐解いていきますので、まずはふんわりと、

「あるまとまった処理を持つコードの塊をクロージャと呼ぶんだなー」

というイメージで読み進めていただいて大丈夫です👌

関数との違い

クロージャと関数は似たような書式を持っていますが、「自身に名前が付いているか」という点に違いがあります。

関数は定義する時に名前が命名されます。対して、クロージャは自身に名前を持ちません。

この特徴から、クロージャのことを「無名関数」と呼ぶこともあります。

【関数の書式例】

func 関数名(引数…){}と記述して定義する

・定義時に名前が命名される(例では「greet」)

				
					// 挨拶メッセージを返却する関数
func greet(name: String) -> String {

    return "こんにちは、 \(name)!"
    
}
				
			

【クロージャの書式例】

・まとまった処理を{}で囲み、変数に代入するなどして使う

・クロージャ自身は名前を持っていない(無名関数)

				
					// 挨拶メッセージを返却するクロージャ
// クロージャ自身は名前を持っていない
{ (name: String) -> String in

    return "こんにちは、 \(name)!"
    
}

// 変数にクロージャを代入するパターン
let greet = { (name: String) -> String in

    return "こんにちは、 \(name)!"
    
}
				
			

クロージャの基本

基本書式

まずは、クロージャの基本書式を確認しておきましょう。

💡私たちが普段目にしているクロージャは、多くの場合、省略形によって簡潔に記述されています。クロージャ本来の書式は、以下のような形で構成されています。

省略形については、後述【クロージャの省略形】で解説しています。

				
					// クロージャの基本書式
{ (引数名: 引数のデータ型) -> 戻り値のデータ型 in

    // 実行したい処理
    
    return 戻り値
}
				
			

クロージャでは「in」の次に実行したい処理を書きます。

この基本書式に当てはめて、例えば次のようにクロージャを定義できます。

Int型の引数を受け取り、引数に2を掛け算して返却するシンプルなクロージャです。

				
					// 基本書式に則ったクロージャの定義
// 変数にクロージャを代入するパターン
let sampleClosure = { (number: Int) -> Int in

    return number * 2 // 引数に2を掛けて返す
}
				
			

クロージャを実際に使用したコードの全体像を以下に挙げます。

クロージャに戻り値がある場合は、例のようにして新しい変数に戻り値を格納することができます。

【全体のコード】

クロージャ使用の全体コード

引数や戻り値を持たないクロージャ

クロージャは、「引数」「戻り値(返り値)」を持たないクロージャとして定義することも可能です。

				
					// 「()」は引数無し、「-> Void」は戻り値無しを表す
{ () -> Void in
    // 処理
    // ...
    // return は省略可能
}

// 戻り値の 「Void」 は 「()」 に書き換え可能
{ () -> () in
    // ...
}
				
			

この場合、処理によって返却する戻り値が存在しないため、returnは省略することができます。

書式に当てはめて、例えば以下のようにクロージャを定義できます。

実行時にデバッグメッセージ「”CodeCandy“」がプリント出力されるクロージャです。

				
					/// 「引数」や「戻り値」を持たないクロージャ
let voidClosure = { () -> Void in

    print("CodeCandy")
    
}
				
			

引数が必要なく、戻り値も無いため、以下の全体コードのvoidClosure()のように、実行時のコードは変数名()のみとなります。

【全体のコード】

クロージャの省略形

省略形について段階的に学ぶ

クロージャは、ある一定の条件を満たす時、コードの記述を簡略化することができます。
この機能によって、コードを簡素に記述でき、可読性も高めることができます。

以下のサンプルコードを用いて、条件によってクロージャが省略されていく様子を見ていきましょう。 

【サンプルコード】

				
					// 引数に2を掛け算して返すクロージャ
let sampleClosure = { (number: Int) -> Int in

    return number * 2
}
				
			

1. returnを省略する

クロージャ内のコードが一行に収まっている時returnを省略できます。

				
					// 条件: クロージャ内の処理が一行である場合
let sampleClosure = { (number: Int) -> Int in

    number * 2 // ⬅︎ returnを省略
}

				
			

2. 戻り値の型を省略する

戻り値の型推論が可能な場合、戻り値の型表記を省略できます。

これで「-> Int」を省略することができました。

				
					// 条件: 戻り値が型推論可能である場合
let sampleClosure = { (number: Int) in // ⬅︎ 「-> Int」を省略

    number * 2
}
				
			

3. 引数の()と型を省略する

引数の型推論が可能な場合、引数の「()」と型表記を省略できます。

以下のように、引数名だけを記述する形となります。見覚えのある形になってきました。

				
					// 条件: 引数が型推論可能である場合
let sampleClosure = { number in // ⬅︎ 「()」と型表記を省略

    number * 2
}
				
			

4. 引数自体を省略する

上記までの省略条件が揃っていれば、さらに引数自体を省略することもできます。

name in」の部分が省略されます。よく見る書き方ですね!

またこの場合、クロージャ内で引数の値を扱うには「$(ダラー)」という記号を使います(後述で解説)。

				
					// 引数自体を省略できる
let sampleClosure = {  // ⬅︎ 「name in」 を省略

    $0 * 2  // 「$0」は第一引数を表す
}

// このような形でも記述される
let sampleClosure = { $0 * 2 }

				
			

「$(ダラー)」ってなに?

ここで新しく出てきた「$0」という記述ですが、これは第一引数のことを表しています。

「$」はSwiftにおいて複数の意味で用いられますが、クロージャの中で使用した場合、省略されている引数にアクセスすることができます。

例えば以下のコードの場合、Int型の第一引数「10」を、「$0」によってクロージャ内で参照しています。

				
					// 「$0」は、第一引数を表す
let sampleClosure = {

    $0 * 2 // 第一引数に2をかける
}

let number = sampleClosure(10)
print(number) // 出力結果: 20
				
			

複数の引数がある場合は、「$1」で第二引数、「$2」で第三引数…といったように指定参照することが可能です。

				
					// 「$1」で第二引数、「$2」で第三引数...というように引数を指定参照できる
let sampleClosure = {

    let a = $0 * 2  // 10 * 2
    let b = $1 * 2  // 5 * 2
    let c = $2 * 2  // 2 * 2

    return a + b + c
}

let number = sampleClosure(10, 5, 2) // 複数の引数を渡す
print(number) // 出力結果: 34
				
			

5. 最終的なコードの確認

では、省略する前と後のクロージャ書式を見比べてみましょう。

				
					// 省略前
let sampleClosure = { (number: Int) -> Int in

    return number * 2
}

// 省略後
let sampleClosure = { number * 2 }
				
			

このようにしてクロージャの記述を省略することで、コードの可読性を高めることができます。

省略形は大変便利な機能である反面、「このコードってなんでこうなってるの…?」 となる要因にもなりやすいです。

多くのクロージャは省略形によって使われているので、早めに慣れてしまいましょう👌

トレイリングクロージャ

トレイリングクロージャとは

Swiftが提供するクロージャの省略形の一つとして、トレイリングクロージャ(Trailing Closure)という省略構文があります。

機能の概要を以下に挙げます。

【トレイリングクロージャの機能概要】

【条件】

 ・イニシャライザの末尾(最後)の引数がクロージャである場合

【機能】

 ・引数のクロージャを「()」の外に移動できる

 ・クロージャの引数名を省略できる

 ・他に引数が存在しない時、「()」を省略できる

トレイリングクロージャの活用例

具体的なコードを使って、トレイリングクロージャの動きを確認してみます。

以下は、SwiftUIのButtonの基本書式の一つです。

上記のイニシャライザには、「action:」と「label:」二つの引数がありますね。そして、どちらの引数もクロージャであることがわかります。

Appleのドキュメントから、上記のButtonのイニシャライザ構成を見てみます。

【Buttonのイニシャライザ】

👉ドキュメント: Button | Apple Developer Documentation

一行として見てみると、イニシャライザの構成が掴みやすいですね。

この場合、末尾の引数は「label:」であり、引数のデータ型「() -> Label」はビューを返却するクロージャの型です。

よって、「末尾の引数がクロージャである」というトレイリングクロージャの適用条件を満たしています。

Buttonにトレイリングクロージャを適用する

では、実際にButtonに対してトレイリングクロージャを適用し、コードの変化を見ていきます。

末尾のクロージャ引数「label:」に注目してください。

右記のトレイリングクロージャでは、引数ラベルが省略され、クロージャが「()」の外に移動しているのが見えるでしょうか。

このように、末尾のクロージャ引数のコードを簡略化して、可読性を高めることができるのがトレイリングクロージャのメリットです。

マルチトレイリングクロージャ

Swift5.3(Xcode12)からの新機能として、マルチトレイリングクロージャ(Multiple Trailing Closure)という省略構文が使えるようになりました。

機能の概要を以下に挙げます。

【マルチトレイリングクロージャの機能概要】

【条件】

 ・引数にクロージャが連続する場合

【機能】

 ・クロージャ引数全てを「()」の外に移動する

 ・最初のクロージャのみ従来の省略記法を適用する

 ・後続のクロージャはラベルを付与した上で表現する

 ・クロージャ以外の引数が存在しない時、「()」を省略できる

条件に挙げている「引数にクロージャが連続する」というパターンは、まさに先述で使用したSwiftUIのButtonが該当しますね。

Buttonにマルチトレイリングクロージャを適用する

では、Buttonに対して「トレイリングクロージャ」「マルチトレイリングクロージャ」それぞれのパターンを適用し、書式の比較をします。

右記のマルチトレイリングクロージャでは、第一引数「action:」のラベルが省略されて、以降の引数である「label:」がラベル表記されていますね。

さらに、イニシャライザにクロージャ以外の引数が存在しないため、「()」も省略されていることがわかります。

これで、全体のコードをよりスッキリさせることができました!

今回は主にSwiftUIのButtonを使いましたが、使用するプログラムによって適切な省略形は変わってくるので、その都度読みやすい省略形を選択していくのが良いかと思います。

クロージャの使用パターン

変数に代入して使う

クロージャは、変数に代入して使うことができます。

ここまでの解説でも活用していたパターンですね。

				
					// クロージャを変数に代入するパターン
let greetClosure = { (name: String) -> String in

    "こんにちは、 \(name)!"
}

// クロージャの実行
let message = greetClosure("太郎")
print(message) // 実行結果: こんにちは、太郎!
				
			

関数の引数として渡す

クロージャは、関数の引数として渡すこともできます。多くのプログラムでも用いられている方法です。

クロージャを引数として持つ関数は、高階関数(Higher-order function)とも呼ばれたりします。

【クロージャを持つ関数の書式】

				
					// 引数にクロージャを持つ関数
func 関数名(引数名: クロージャの型) {

    // 処理...
}
				
			

例えば以下のコード例では、関数の引数のデータ型に「() -> Void」を設定しています。

このように定義しておくことで、関数実行時にクロージャを渡せるようになります。

				
					// クロージャを引数に持つ関数の定義
func sampleFunction(closure: () -> Void) {  // ⬅︎ 引数にクロージャの型を定義する

    closure() // 受け取ったクロージャを実行
}

// 使用例
sampleFunction(closure: { () -> Void in

    print("CodeCandy")
})

// 使用例(省略形)
sampleFunction {

    print("CodeCandy")
}
				
			

関数実行側で記述したクロージャが、引数として関数内に渡っていくイメージが掴めるとOKです。

【全体のコード】

まとめ

以上、Swiftにおける「クロージャ」についてでした!

要点をまとめておきます。

「{ }」で囲まれた、まとまったコード処理を実行できる

条件下でコードを簡潔にできる(省略形)

・自身に名前を持たない(無名関数)

・変数への代入や、関数の引数として渡すことが可能

書式に少しクセがあったり、様々な省略形があったりで、「このコードってどういうこと…?」となりやすいクロージャですが、プログラミングにおいて無くてはならない超重要な機能です。ぜひ本記事を元に、使い慣らしていきましょう👌

参考になりましたら幸いです。

\  SHARE  /

Twitter
Facebook
Email

✏️ アプリ開発が学べる勉強会を開催中! 

CodeCandyではアプリ開発を学ぶための勉強会を定期開催しています。
学習する習慣を身につけたい、他の参加者と作業したい、アプリ開発の基本をマスターしたい、という方のために無料で学べる勉強会です。
グループにメンバー登録して頂くと、イベント開催時にメールで通知されます。

▶️グループのメンバーとして参加する

徹底した基礎学習からマスターするiPhoneアプリ開発集中オンライン講座開講!

本書「iPhoneアプリ開発集中講座」を執筆している現役エンジニア講師陣が直接に指導!
基礎、課題実習で実践力を鍛えて、オリジナルアプリ公開までチャレンジ!
充実した転職支援もあるので、エンジニアへ転職したい人にもおすすめです!

まずは、現役エンジニアに相談できる無料相談をご利用ください。

By 中川 賢亮

2022年2月よりSwift学習を始め、4月からiOSアプリ開発オンラインスクール「CodeCandy」にてアプリ開発を学ぶ。 2023年10月に個人開発アプリ「unico」をリリース。現在はアプリの機能アップデートをしながら、スクール運営の技術ブログの執筆や、出版書籍の入稿チェック・デバッグにも携わる。