MENU

【Swift入門】クロージャとは?基本から実践まで徹底解説

Swiftを学習していると必ず出会う「クロージャ」。最初は難しく感じるかもしれませんが、理解すればコードがグッと読みやすく、書きやすくなります。

この記事では、Swift初心者の方でも理解できるように、クロージャの基本から実践的な使い方まで、サンプルコードを交えながら丁寧に解説します。

目次

クロージャとは?

クロージャとは、処理をひとまとめにしたコードブロックのことです。変数に代入したり、関数の引数として渡したりできます。

他のプログラミング言語では「ラムダ式」や「無名関数」と呼ばれることもあります。

関数との違い

通常の関数とクロージャは似ていますが、クロージャには名前がありません。そのため、必要な場所で直接定義して使うことができます。

// 通常の関数
func addNumbers(a: Int, b: Int) -> Int {
    return a + b
}

// クロージャ
let addClosure = { (a: Int, b: Int) -> Int in
    return a + b
}

// どちらも同じように呼び出せる
addNumbers(a: 3, b: 5)  // 8
addClosure(3, 5)        // 8

クロージャの基本構文

クロージャの基本的な書き方は以下の通りです。

{ (引数名: 型) -> 戻り値の型 in
    処理
    return 戻り値
}

具体例

let greet = { (name: String) -> String in
    return "こんにちは、\(name)さん!"
}

print(greet("太郎"))  // "こんにちは、太郎さん!"

クロージャの3つの形式

Swiftでは、クロージャは3つの形式で存在します。

1. グローバル関数

名前があり、値をキャプチャしない普通の関数です。

func multiply(a: Int, b: Int) -> Int {
    return a * b
}

2. ネストされた関数

関数の中で定義された関数で、外側の関数の値にアクセスできます。

func makeIncrementer(increment: Int) -> () -> Int {
    var total = 0
    
    func incrementer() -> Int {
        total += increment
        return total
    }
    
    return incrementer
}

let addTwo = makeIncrementer(increment: 2)
print(addTwo())  // 2
print(addTwo())  // 4

3. クロージャ式

簡潔な構文で書ける無名のクロージャです。

let square = { (number: Int) -> Int in
    return number * number
}

クロージャの実践的な使い方

配列操作での活用

クロージャは配列の操作で頻繁に使用されます。

map – 各要素を変換

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

filter – 条件に合う要素を抽出

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let evenNumbers = numbers.filter { (number: Int) -> Bool in
    return number % 2 == 0
}
print(evenNumbers)  // [2, 4, 6, 8, 10]

reduce – 要素を集約

let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce(0) { (result: Int, number: Int) -> Int in
    return result + number
}
print(sum)  // 15

sorted – ソート

let names = ["太郎", "花子", "次郎", "美咲"]
let sortedNames = names.sorted { (name1: String, name2: String) -> Bool in
    return name1 < name2
}
print(sortedNames)  // ["次郎", "太郎", "美咲", "花子"]

クロージャの省略記法

Swiftのクロージャは、様々な方法で簡潔に書くことができます。

1. 型推論による省略

コンパイラが型を推測できる場合、型を省略できます。

// 元のコード
let doubled = numbers.map { (number: Int) -> Int in
    return number * 2
}

// 型を省略
let doubled = numbers.map { number in
    return number * 2
}

2. 単一式でのreturn省略

クロージャの中身が1行だけの場合、returnを省略できます。

// returnを省略
let doubled = numbers.map { number in
    number * 2
}

3. 引数名の省略(短縮引数名)

引数名の代わりに $0, $1, $2 といった短縮名を使えます。

// 引数名を省略
let doubled = numbers.map { $0 * 2 }

// 複数の引数がある場合
let sum = numbers.reduce(0) { $0 + $1 }

4. 演算子メソッド

演算子自体を渡すこともできます。

let sum = numbers.reduce(0, +)
print(sum)  // 15

比較:すべての書き方

let numbers = [1, 2, 3, 4, 5]

// 完全な形
let result1 = numbers.map { (number: Int) -> Int in
    return number * 2
}

// 型推論を使用
let result2 = numbers.map { number in
    return number * 2
}

// returnを省略
let result3 = numbers.map { number in number * 2 }

// 短縮引数名を使用
let result4 = numbers.map { $0 * 2 }

// すべて同じ結果: [2, 4, 6, 8, 10]

トレイリングクロージャ

関数の最後の引数がクロージャの場合、括弧の外に書くことができます。これをトレイリングクロージャと呼びます。

// 通常の書き方
let result = numbers.map({ $0 * 2 })

// トレイリングクロージャ
let result = numbers.map { $0 * 2 }

引数がクロージャだけの場合は、括弧も省略できます。

値のキャプチャ

クロージャは、定義された場所の周囲の変数を「キャプチャ」して保持できます。

func makeCounter() -> () -> Int {
    var count = 0
    
    let counter = {
        count += 1
        return count
    }
    
    return counter
}

let myCounter = makeCounter()
print(myCounter())  // 1
print(myCounter())  // 2
print(myCounter())  // 3

この例では、クロージャが count 変数をキャプチャしており、関数の実行が終わった後も値を保持し続けます。

非同期処理でのクロージャ

クロージャは非同期処理でよく使われます。

// ネットワークリクエスト
URLSession.shared.dataTask(with: url) { data, response, error in
    if let data = data {
        // データの処理
        print("データを取得しました")
    }
}.resume()

// アニメーション
UIView.animate(withDuration: 0.5) {
    view.alpha = 0
} completion: { finished in
    print("アニメーション完了")
}

エスケープクロージャ

関数の実行が終わった後にクロージャが呼ばれる場合、@escapingキーワードを付ける必要があります。

var completionHandlers: [() -> Void] = []

func saveCompletionHandler(handler: @escaping () -> Void) {
    completionHandlers.append(handler)
}

saveCompletionHandler {
    print("後で実行されます")
}

非エスケープクロージャ(デフォルト)は、関数内でのみ使用され、関数の実行が終わると破棄されます。

よくある使用例

1. ボタンのアクション

button.addAction(UIAction { action in
    print("ボタンがタップされました")
}, for: .touchUpInside)

2. カスタムソート

struct Person {
    let name: String
    let age: Int
}

let people = [
    Person(name: "太郎", age: 25),
    Person(name: "花子", age: 30),
    Person(name: "次郎", age: 20)
]

let sortedByAge = people.sorted { $0.age < $1.age }

3. 条件によるフィルタリング

let products = [
    ("りんご", 120),
    ("みかん", 80),
    ("バナナ", 150),
    ("ぶどう", 200)
]

let affordable = products.filter { $0.1 <= 150 }
// [("りんご", 120), ("みかん", 80), ("バナナ", 150)]

クロージャを使う際の注意点

1. 循環参照に注意

クロージャ内で self を使用する場合、循環参照が発生する可能性があります。

// 悪い例
class ViewController {
    var closure: (() -> Void)?
    
    func setupClosure() {
        closure = {
            self.doSomething()  // 循環参照
        }
    }
}

// 良い例
class ViewController {
    var closure: (() -> Void)?
    
    func setupClosure() {
        closure = { [weak self] in
            self?.doSomething()  // weakで参照
        }
    }
}

2. 可読性とのバランス

短縮記法は便利ですが、過度に使用するとコードが読みにくくなります。

// 読みにくい
let result = data.filter { $0.1 > 100 }.map { $0.0 }.sorted { $1 < $0 }

// 読みやすい
let expensiveProducts = data.filter { price in price.1 > 100 }
let productNames = expensiveProducts.map { product in product.0 }
let sortedNames = productNames.sorted { name1, name2 in name2 < name1 }

まとめ

この記事では、Swiftのクロージャについて基本から実践まで解説しました。

重要なポイント

  • クロージャは処理をまとめたコードブロック
  • 配列操作で頻繁に使用される(map、filter、reduceなど)
  • 様々な省略記法で簡潔に書ける
  • 非同期処理でよく使われる
  • 循環参照に注意が必要

クロージャを使いこなせるようになると、Swiftのコードがより簡潔で読みやすくなります。最初は完全な形で書いて、慣れてきたら少しずつ省略記法を使っていくのがおすすめです。

実際のアプリ開発では、配列の操作や非同期処理など、様々な場面でクロージャを使うことになります。この記事を参考に、ぜひクロージャを活用してみてください。

プログラミングの独学におすすめ
プログラミング言語の人気オンラインコース
独学でプログラミングを学習している方で、エラーなどが発生して効率よく勉強ができないと悩む方は多いはず。Udemyは、プロの講師が動画で実際のプログラムを動かしながら教えてくれるオンライン講座です。講座の価格は、セール期間中には専門書籍を1冊買うよりも安く済むことが多いです。新しく学びたいプログラミング言語がある方は、ぜひUdemyでオンライン講座を探してみてください。
目次