【Swift】クロージャの意味や使い方をゼロから解説

【Swift】クロージャって?

Swiftには、変数や処理を独自のスコープに閉じ込め、複雑なコードを簡潔に記述できるクロージャが存在します。

この記事では、筆者が疑問に感じた

  • クロージャって何?
  • クロージャってどうやって書くの?
  • 関数とクロージャは何が違うんだ?
  • クロージャはどんなところで使われるの?

をもとにクロージャに対する個人的な理解をまとめました。

記事を読んで、ぜひ参考にしてみてください!

目次

クロージャに対する第一印象

筆者はSwiftに対する理解を深めるべく、UdemyでSwiftをゼロから学んでいます。

Swift基礎編でクロージャが紹介されていまいたが、こんな印象を受けました。

初心者

この書き方は何なんだ😱
関数が変数に割り当てられているのか!?
関数とクロージャは何がどう違うんだ、、、
クロージャとやらは、どういうところで使われるんだ
書き方難しくないか😇

クロージャの意味や使い方の理解が難しいなと思ったので、「個人的に理解した方法を記事にしてしまおう!」という経緯で書きました。

クロージャの意味や使い方

まずはクロージャが何者なのか、見ていきましょう。

クロージャとは

クロージャは、変数や定数を独自のスコープ内に閉じ込めた独立した処理ブロックです。

  • 一般的な関数
  • ifやswitchのような条件分岐
  • forやwhileのようなループ

これらのコードブロックには、共通する性質があります。

それが、独自のスコープを持っているということです。

以下の例のように、関数内で定義した変数は外から参照することができません。

func myAddition(num1: Int, num2: Int) -> Int {
    let total = num1 + num2
    return total
}
myAddition(num1: 1, num2: 2) // 3
print(total) // Cannot find 'total' in scope

関数myAdditionの内部で定義した変数totalは、外部からは見えていないです。
言い換えると、関数が独自のスコープをもっているということです。

Swiftでは、この関数のように変数や処理をひとまとまりに扱えるクロージャという概念が存在します。

上の関数をクロージャで書いてみると以下のコードになります。

let myAddition = { (num1: Int, num2: Int) -> Int in
    let total = num1 + num2
    return total
}
myAddition(1, 2) // 3

構造を比較してみると、どちらもよく似ていますよね〜。

実を言うと、関数もクロージャの一種なのです。

え、関数もクロージャの一種なの😲
似たもの同士ということか〜
いやでも、関数とクロージャは何が違うんだ?

クロージャと関数の大きな違い

クロージャは関数と似たもの同士であり、どちらも変数や処理をひとまとまりに扱える独立したブロックであることを紹介しました。

では、クロージャと関数は何が違うのでしょうか?

結論から言えば、複雑なコードを単純化し、すっきりとしたコードにできるかどうかが最大の違いです。

早速比較してみましょう。
2つの値num1num2の加算処理myAdditionprocess_myAdditionが行うことを例にしてみました。

func myAddition(num1: Int, num2: Int) -> Int {
    return num1 + num2
}
func process_myAddition() {
    let total = myAddition(num1: 1, num2: 2)
    print(total)
}

process_myAddition() // 実行結果: 3
func processClosure(closure: (Int, Int) -> Int) {
    let total = closure(1, 2)
    print(total)
}

processClosure { $0 + $1 } // 実行結果: 3

比較してみてどうでしょうか。
同じコードですが、少しすっきりしたんじゃないかなと思います。

コードの良し悪しは別にして、確実にコード量は少なくなり、少ないコード量で処理を実現できています。

これがクロージャの最大のメリットかと思います。

myAdditionがまるまる消えてる😳
なるほどね〜🤔
クロージャを使うと、実現したい処理を簡潔に書くことができる、と。

クロージャの基本的な書き方

Swiftのクロージャはこのように書きます。

{(引数名: 型) -> 返り値の型 in
    処理内容
}

先ほどの関数myAdditionをクロージャに落としこむなら以下の書き方になります。

let myAddition = {(num1: Int, num2: Int) -> Int in
    return num1 + num2
}

このクロージャmyAdditionは、Int型の引数num1とInt型の引数num2を受け取って、それらの合計をInt型で返すクロージャです。

クロージャの基本的な使い方

ここでは簡単に、以下の2つのパターンでクロージャの基本的な使い方を解説します。

  1. クロージャのみ
  2. クロージャと関数の組み合わせ

クロージャのみ

この場合、関数を呼び出す場合と同じように実行します。

let myAddition = { (num1: Int, num2: Int) -> Int in
    return num1 + num2
}
myAddition(1, 2) // 3

クロージャは関数と違い、外部引数名を定義することができません。
クロージャの呼び出し時は、引数名を省略することが可能です。

クロージャと関数の組み合わせ

両者を組み合わせて使う場合、関数の引数にクロージャを指定して呼び出す方法が挙げられます。

/// 2つの整数を足し合わせてその結果を返すクロージャを定義
let myAddition = {(num1: Int, num2: Int) -> Int in
    return num1 + num2
}
/// クロージャを受け取ってクロージャの実行結果を出力する関数
func processClosure(closure: (Int, Int) -> Int) {
    let total = closure(1, 2)
    print(total)
}
processClosure(closure: myAddition) // 3

上の例では、関数processClosureがクロージャmyAdditionを引数として受け取っています。

中で何が行われているか順を追って見ていきましょう。

STEP
関数がクロージャを引数として受け取る

関数processClosureはクロージャmyAdditionclosureに代入します。

STEP
関数内でクロージャを実行

関数processClosure内のclosure(1, 2)は、myAddition(1, 2)と等価になります。

関数processClosure内から見て、親スコープにmyAddition(1, 2)が定義されているため、関数内のクロージャが実行されます。

実行結果3が定数totalに格納されます。

定数totalが出力されます。

この例だと、関数の内部引数のクロージャに対して、処理方法を外から渡しているイメージか👏

Swiftのクロージャはシンプルに書ける

クロージャをよりシンプルに書くために、以下3つを使います。

  • 型を省略:クロージャにあらかじめ型指定を行い、クロージャを代入する際に型を省略
  • 引数の簡略化
  • 暗黙的リターン

型の省略

クロージャにあらかじめ型指定すれば、 変数にクロージャを代入する際に型指定を省略することが可能です。

let myAddition: (Int, Int) -> Int // あらかじめ型指定
myAddition = {(num1, num2) in // 型の省略
    return num1 + num2
}

引数の簡略化

クロージャの引数を$[インデックス]の形で簡略化することが可能です。

let myAddition: (Int, Int) -> Int // あらかじめ型指定
myAddition = { 
    return $0 + $1 // 型の省略 + 引数の簡略化
}

クロージャmyAdditionは、受け取った引数を順番に$0,$1と割り当てます。

暗黙的リターン

関数およびクロージャのコードブロックに1つしか式が含まれていない場合、関数およびクロージャはその式を暗黙的に返します。
したがって、returnは必要ありません。

let myAddition: (Int, Int) -> Int // あらかじめ型指定
myAddition = { $0 + $1 } // 型の省略 + 引数の簡略化 + 暗黙的リターン

いろんな技を駆使してここまでスマートにかけるのか👏

クロージャはどういうところで使われているの?

Swiftでは配列を操作するメソッドが存在し、その操作にクロージャがよく出てきます。

配列を操作するメソッドmapでクロージャを使ってみます。

map

この関数は、与えられた配列の各要素に対してクロージャを適用した結果を配列に格納して返す関数です。

以下の例は、配列numbersの各要素を2倍したものを配列doubledに格納しています。

let numbers: [Int] = [1, 2, 3, 4, 5]
let doubled: [Int] = numbers.map({ (number: Int) -> Int in
    return number * 2
})
print(doubled) // 実行結果: [2, 4, 6, 8, 10]

まとめ

この記事では、クロージャの意味や使い方について解説しました。

  • クロージャは、変数や定数を独自のスコープ内に閉じ込めた独立した処理ブロック
  • クロージャには、コードをシンプルに書ける仕様が備わっている
  • 関数の引数にクロージャを指定して呼び出すことができる
  • mapやfilterなどの配列操作に関する関数でクロージャは使われている

参考文献
Closures — The Swift Programming Language (Swift 5.7)
クロージャとは | Swiftの始め方
[Swift]クロージャーの使い方
【Swift】クロージャとは?関数との違いとキャプチャの意味

【Swift】クロージャって?

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

よぴのアバター よぴ Swift / iOSエンジニア

ゼロから始めるSwift/iOSエンジニア
デザインや開発に携わる人たちと繋がって、学び合える関係を作りたいです。

▷ iOSアプリ個人開発
▷ 2022/10/01からスタート
▷ 制作過程や学んだことをブログに載せていきます

目次