0Pricing
Swift Academy · レッスン

クロージャ

クロージャ

「クロージャ」はCoddyKit上の無料Swift Academyレッスンです。 これはレッスン1/1です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはSwift Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Swift Academyコースには全1レッスンが含まれています。

はじめに

こんにちは。

クロージャは自己完結型のコードブロックです。クロージャはコードにパラメータとして渡したり、コード内で使用したりできます。

クロージャは関数の一種です。コード内で渡して使用できます。他のプログラミング言語のラムダに似ています。

メモリ管理

特に重要なのは、クロージャが定義されたコンテキストにある定数や変数の値を保持できることです。

この保持はclosing-overと呼ばれます。

Swiftは、この保持中のメモリ管理を自動的に行います。

クロージャの種類

クロージャには次の種類があります。

  1. グローバル関数は名前付きクロージャです。これらの関数は値を保持しません。
  2. ネストした関数は、それを囲む関数の名前や値を保持できます。
  3. クロージャ式は匿名クロージャで、簡潔な構文によって周囲のコンテキストのデータを保持できます。

クロージャの構文

クロージャの構文は、できるだけ簡潔になるよう設計されています。主な特徴は以下のとおりです。

  • クロージャのパラメータは推論できます。
  • 戻り値の型は推論できます。
  • クロージャの戻り値は1行で記述できます。
  • 引数名は$0や$1のような短縮記法で表せます。
  • トレーリングクロージャ構文を使用できます。

補足

クロージャ式とは、構文と簡潔さを重視してクロージャをインラインで記述することです。

クロージャの構文

クロージャの構文を以下のサンプルコードに示します。

{ (parameters) -> return type in
    statements
}


func backward(_ s1: String, _ s2: String) -> Bool {
    return s1 > s2
}
//If we didn't use closures, our code structure would be as follows.

var reversedNames = names.sorted(by: backward)

クロージャの構文

しかし、クロージャを使えば、このような不要な名前付きグローバルクロージャを定義する必要はありません。

パラメータ内に簡単なクロージャ式を定義するだけで十分です。例を見てみましょう。

let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
let reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
    return s1 > s2
})

print(reversedNames)

///sorted method takes closure as parameter.

inキーワード

inキーワードから、クロージャの本体が始まることが分かります。

inキーワードの前に、クロージャが受け取るパラメータと戻り値の型を定義する必要があります。

コンテキストから型を推論する

コンテキストから型を推論する

上の例では、sortedメソッドがクロージャを受け取るため、パラメータと戻り値の型は推論によって決定できます。

したがって、それらを別途定義する必要はありません。

この例では、names配列の値がStringであるため、クロージャが受け取るパラメータもString型だと推論されます。

同様に、並べ替えに使用するクロージャはBool型を返す必要があるため、戻り値の型はBoolと推論されます。

例

これにより、以下のように簡潔に記述できます。

戻り値の型が推論されるため、コードから矢印も省略できます。

let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
let reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
    return s1 > s2
})

print(reversedNames)

短縮構文

コードを読む人を混乱させないため、短縮構文ではなく長い構文を使用するほうが望ましい場合があります。

上記の構文はさらに短縮できます。

returnキーワードを削除すると、クロージャ式で戻り値を短縮できます。

/*
In this example, the return keyword has been extracted.
It already has single expression. 
There is no need to express it as an extra with return.
*/

var names = ["Coddy", "Kit"]
let reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
print(reversedNames)

例

この例では、returnキーワードを省略しています。

すでに単一の式になっているため、returnを使って明示的に記述する必要はありません。

reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )

名前の短縮記法

構文はさらに短くできます。上の例では、クロージャが2つのパラメータ(s1とs2を含む)を受け取ることを示しました。これらの型は推論されます。

これらのパラメータを短縮記法で表し、定義する手間を省けます。

さらに、パラメータ定義も戻り値の指定もないため、inキーワードも省略できます。

let names = ["Coddy", "Kit"]
let reversedNames = names.sorted(by: { $0 > $1 } )
print(reversedNames)

// In this example $0 will shortcut the first parameter and $1 will shortcut the second parameter.

例の短縮

var names = ["Coddy", "Kit"]

let reversedNames = names.sorted(by: >)
print(reversedNames)
// Greater than operator takes two parameters and the rotation type is squat.

トレーリングクロージャ

トレーリングクロージャ

クロージャ関数に渡すパラメータが関数の最後の引数で、クロージャ式が長い場合は、トレーリングクロージャ構文として記述すると便利です。

トレーリングクロージャはシンタックスシュガーとして使用できます。コードの可読性を高めるために使われます。

ほとんどのiOSメソッドでは、最後のパラメータがクロージャです。

最後のパラメータ

例えば、この例では最後のパラメータがクロージャです。

class Animator {
    class func animate(withDuration duration: Double, animations: () -> Void) {
        animations()
    }
}

class MyViewController {
    func start() {
        // With the traditional method, we can call this fund as follows.
        Animator.animate(withDuration: 1, animations: { [unowned self] in
            print("red")
        })
    }
}
MyViewController().start()

上のコードは読みにくいため、次のように記述できます

上のコードは読みにくいため、次のように記述できます。

import Foundation
Thread.sleep(forTimeInterval: 1)
print("Background color changed to red")

例

トレーリングクロージャ構文を使用すると、引数ラベルを記述する必要はありません。

クロージャが呼び出し構文の一部になったためです。

func someFunctionThatTakesAClosure(closure: () -> Void) {
    // function body goes here
}

// Here's how you call this function without using a trailing closure:
someFunctionThatTakesAClosure(closure: {
    // closure's body goes here
})

// Here's how you call this function with a trailing closure instead:
someFunctionThatTakesAClosure() {
    // trailing closure's body goes here
}

構文

トレーリングクロージャ構文は、シンタックスシュガーと考えることができます。コードを読みやすくします。

/*
In the example below, animations
are expressed as closure.
*/
public class func animate(withDuration: TimeInterval, animations: () -> Void)

// In traditional usage, we need to make a call like the following.
UIView.animate(withDuration: 1, animations: { [unowned self] in
    self.view.backgroundColor = UIColor.red
})

/*
In this way, both the readability of the code
increases and we avoid the use of extra brackets.
If it is as above and the last parameter of the method 
is closure, Swift allows to make a call like the one below.
*/

UIView.animate(withDuration: 1) { [unowned self] in
    self.view.backgroundColor = UIColor.red
}

以下のコードを使ってコードをテストできます

以下のコードを使ってコードをテストできます。


func greetThenRunClosure(name: String, closure: () -> ()) {
    print("Hello, \(name)!")
    closure()
}

greetThenRunClosure(name: "Paul") {
    print("The closure was run")
}

クロージャによる値のキャプチャ

クロージャによる値のキャプチャ

クロージャを囲む環境の変数の値を保持できます。クロージャ自身の本体から、それらの変数の値を変更したりアクセスしたりすることもできます。

これらの変数が元々定義されていたスコープの外側であっても可能です。

続き

一般に、クロージャ式と同様に、ネストした関数でもこのデータを保持できます。外側の関数のデータを保存できます。

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}

let incrementByTen = makeIncrementer(forIncrement: 10)

print(incrementByTen())
// returns a value of 10
print(incrementByTen())
// returns a value of 20
print(incrementByTen())
// returns a value of 30

続き

クロージャをクラスのプロパティとして代入すると、そのクロージャはこのインスタンスへの参照を保持するようになります。

そのため、クロージャとインスタンスの間に強い参照サイクルが形成されます。

メモリリークには特に注意が必要です。

警告

クロージャと関数は参照型です。

@escaping

クロージャを非同期で使用する多くの場面で、この記法が必要になります。

Swiftの初期バージョン(1と2)では、Swiftのパラメータにおけるクロージャはescapingでしたが、後にnon-escapingになりました。クロージャをnon-escapingとして渡す必要がある場合は、明示的に指定する必要がありました。

以降のバージョンでは、メモリ使用量を抑えるため、クロージャパラメータはnon-escapingになりました。

現在、escapingなクロージャを定義したい場合は、@escapingキーワードを付ける必要があります。

@ESCAPINGキーワード

@ESCAPINGキーワード

関数の実行が終了した後も、その関数に渡したクロージャを呼び出せるようにしたい場合は、@escapingキーワードを付ける必要があります。

デフォルトで関数に渡されるクロージャは参照型ですが、参照カウントは増加しません。呼び出し時にこのクロージャのライフタイムが終了するため、参照カウントを追加で増やす必要がないからです。

しかし、関数の呼び出し後もパラメータとして渡したクロージャが必要な場合は、@escapingキーワードを使って表す必要があります。

nonEscaping

まず、nonEscapingクロージャの動作を見てみましょう。

このクロージャを関数のパラメータとして渡すと、関数の本体でクロージャが処理され、その後関数が戻ります。

関数が戻ると、つまり実行が完了すると、クロージャはスコープ外となり、メモリから解放されます。

エスケーピングクロージャの使用

エスケーピングクロージャの使用

クロージャ関数を引数として渡すと、クロージャ関数が戻って実行が完了した後も、そのクロージャは保持され続けます。

クロージャが実行されるまで、メモリ領域を占有します。エスケーピングクロージャには複数の種類があります。

おめでとうございます

おめでとうございます 🎉

これでレッスンは終了です。 

次のレッスンでお会いしましょう 😎

クロージャ — イラスト29

よくある質問

「クロージャ」レッスンは無料ですか?

はい。「クロージャ」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Swift Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Swift Academyコースには全1レッスンが含まれています。

「クロージャ」で何を学びますか?

クロージャ ブラウザで直接実行するハンズオンコードでSwift Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Swift Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのSwift Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/1です。

「クロージャ」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このSwift Academyレッスンでコードを書いて実行できますか?

はい。すべてのSwift Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

← Swift Academyに戻る