[Swift]エンクロージャを閉じる


Swift - Closure


Closer-エンクロージャ


SWIFT公式文書:
Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.
翻訳すると、SWIFTの「クローズ」(Closer)は、C、Objective-C、その他の言語のRamdaに類似したコードブロックであり、コードで伝達または使用することができる.
また、モジュールは定数または変数の参照をキャプチャおよび保存し、すべてのメモリを処理することもできます.
モジュールはどのような機能を持つコードブロックであり、類似の機能を持つコードブロック関数とはどのような関係があるのだろうか.

関数は、モジュールの特定の形式でモジュールに含まれる関係です.
上記のいずれかの条件を有するモジュールを関数と呼ぶ.

Close Expression-モジュール表示


モジュール化表現は、構文を最適化する方法がいくつか用意されているため、コードの明瞭性と意図を犠牲にすることなく構文を圧縮することができます.このため、SWIFTで記述されたコードを初めて表示すると、さまざまな形で縮小されたモジュールを見て戸惑うことがあります.
関数のパラメトリックモジュールにより,様々な異なるモジュール表現方法を理解する.

sorted(by:)

sorted(by:)関数は、SWIFTの標準ライブラリで定義された関数であり、指定されたアレイをユーザがスキップしたエンクロージャに整列させて戻ります.
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
Stringタイプ要素のアレイ「names」を使用してソートします.sorted(by:)関数がパラメータとして受信されるエンクロージャは、アレイ内の要素と同じ2つのタイプの因子を有するエンクロージャである.
このモジュールの戻り値の意味は、最初のパラメータが2番目のパラメータの前にある必要があるかどうかを示します.
func backward(_ s1: String, _ s2: String) -> Bool {
    return s1 > s2
}
var reversedNames = names.sorted(by: backward)
// reversedNames is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
sorted(by:)関数は、パラメータ伝達backward(_:_:)の規則的降順に配列され、アレイに戻る.
上記のように関数を定義して渡すことができますが、エンクロージャをインラインエンクロージャとしてより簡潔に使用する方法を見てみましょう.

Close Expression Syntax-モジュール表現



モジュールのパラメータにdefault値を持つことはできません.0...10に類似した形状であってもよいし、tuple形状であってもよい.
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
    return s1 > s2
})
以上のコードは、backward(_:_: )関数をインラインモジュールとして表す例である.
上記のモジュールおよびbackward(_:_: )関数は、同じパラメータおよび戻りタイプを有するが、モジュールのパラメータは、括弧{}に定義されていることがわかる.inは、モジュールのパラメータと戻りタイプ定義が完了し、本体部分が開始されることを示しています.
上記コードの本体部分が短いため、inの後に1行をスペースなく表示することができる.
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )

Inforring Type From Context-コンテキスト内のタイプ推定

sorted(by:)は、パラメータ伝達されたエンクロージャタイプが(String, String) -> Boolであることが知られているので、エンクロージャの定義では(String, String)およびBoolを省略することができる.
エンクロージャで使用されるすべてのタイプを推定できるため、パラメータのタイプとパラメータを含むカッコと戻り矢印(->)を省略することもできます.
reversedNames = names.sorted(by: {s1, s2 in return s1 > s2})
関数またはメソッドのパラメータとして組み込みモジュールを使用する場合、パラメータと戻りタイプは常に省略できます.

Implicit Returns from Single Expression Closes-単一表示モジュールから暗黙的に戻る


単一表現モジュールは、戻りキーワードreturnを省略することができる.
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
sorted(by:)のパラメータモジュールはBoolを返す必要があるので、returnを省略することができる.

Shorthand Argument Name-省略パラメータ名


SWIFTは、内蔵モジュールの自動サムネイル名を指定します.
略語名はパラメータの順序で$0, $1, $2, ...として使用されます.
サムネイル名を使用すると、モジュール定義のパラメータ定義をスキップし、サムネイルパラメータの数とタイプを自動的に参照できます.
パラメータ定義を省略する場合は、モジュール表現において本体部分のみであるため、inを省略してもよい.
reversedNames = names.sorted(by: { $0 > $1 })

Operatorメソッド-演算子メソッド


モジュール表現を現在の方法で簡略化していますが、非常に簡潔であるため、理解できない人もいるかもしれません.Stringの比較演算子>, <, ==は、2つのパラメータを受け入れてBoolを返すように定義されているので、演算子をキャビネットに渡すだけでよい!
reversedNames = names.sorted(by: >)
省略tomoto?簡潔?

Trailing Closes-背面キャビネット


関数の最後のパラメータをモジュールに渡す必要があり、モジュールが長い場合は、後部モジュールを使用することが望ましい.
後部キャビネットとは、関数の最後のパラメータがキャビネットである場合、関数呼び出しカッコの後にパラメータ名を使用せずにキャビネットを定義することです.
func someFunctionThatTakesAClosure(closure: () -> Void) {
    // function body goes here
}

// 위 함수를 후위 클로저를 사용하지 않고 호출

someFunctionThatTakesAClosure(closure: {
    // closure's body goes here
})

// 후위 클로저 사용

someFunctionThatTakesAClosure() {
    // trailing closure's body goes here
}
上記の部分では、sorted(by:)もバックエンクロージャで表すことができる.
reversedNames = names.sorted() { $0 > $1 }
上述したように、関数の一意のパラメータがCloserであり、後置Closerを使用する場合、関数呼び出し部の()括弧を省略することができる.
reversedNames = names.sorted { $0 > $1 }

取得値-取得値


定義されたコンテキスト内の定数または変数をキャプチャすることで、モジュール本体でモジュールを参照または変更できます.
SWIFTの最も簡単な例は、関数に他の関数を含むネストされた関数である.
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}
makeIncrementer(forIncrement:)関数の戻りタイプは() -> Intで、単純な値ではありません.makeIncrementer(forIncrement:)関数は、runningTotalという変数を定義し、Intパラメータamountを受け入れます.
func incrementer() -> Int {
    runningTotal += amount
    return runningTotal
}
incrementer()関数を個別に表示する場合にのみ、この関数にはパラメータはありませんが、runningTotalおよびamountは定義されていませんが、正常に動作します.
これは、この関数を含む関数においてrunningTotalおよびamountが取り込まれるためである.
使用例を見てみましょう.
let incrementByTen = makeIncrementer(forIncrement: 10)
このように作成された関数
incrementByTen()
// returns a value of 10
incrementByTen()
// returns a value of 20
incrementByTen()
// returns a value of 30
正常に動作.
let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// returns a value of 7
前述したように、異なるパラメータを使用して定義すると、新しい値が取得され、異なる結果が表示されます.

Closures Are Reference Types


上記の例では、incrementByTen()は定数であるが、runningTotal変数の値を変更し続けることができる.
これは、SWIFTでは関数とモジュールが参照タイプであるため、定数や変数に割り当てると、実際の値ではなく参照が割り当てられます.

Escaping Closure


非同期またはcompletionHandlerとして使用されるように、関数の外で実行される(関数が終了した後)エンクロージャは、パラメータタイプの前に@escapingのキーワードを指定する必要があります.
var completionHandlers = [() -> Void]()
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}
上記の関数では、パラメータとして渡されるcompletionHandlerは、@escapingのキーワードを指定する必要があります.そうしないと、コンパイルエラーが発生します.
さらに、@escapingキーワードを使用するモジュールはselfに明確に言及しなければならない.
func someFunctionWithNonescapingClosure(closure: () -> Void) {
    closure()
}

class SomeClass {
    var x = 10
    func doSomething() {
        someFunctionWithEscapingClosure { self.x = 100 }
        someFunctionWithNonescapingClosure { x = 200 }
    }
}

let instance = SomeClass()
instance.doSomething()
print(instance.x)
// Prints "200"

completionHandlers.first?()
print(instance.x)
// Prints "100"

自動クローズ-自動エンクロージャ


自動キャビネットは、パラメータ値のないキャビネットであり、別の関数の伝達パラメータとして使用される表現を囲むことができます.
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// Prints "5"

let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// 정의 될때 실행되지 않음
// Prints "5"

print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// Prints "4"
// () -> String 인자가 없는 클로저를 인자로 받는 함수 serve
func serve(customer customerProvider: () -> String) {
    print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } )
// Prints "Now serving Alex!"
次の図に示すように、パラメータ値は@autoclosureキーワードを使用して自動的にエンクロージャに変換されます.
// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
    print("Now serving \(customerProvider())!")
}
// {} 중괄호 없이 표현 전달
serve(customer: customersInLine.remove(at: 0))
// Prints "Now serving Ewa!"