[iOSノート]Swiftでのクローズ(Closures)

5386 ワード

閉パケットは自己包含関数ブロックであり、Swiftの閉パケットはCおよびObjective-Cのブロック(blocks)および他のいくつかのプログラミング言語の匿名関数と比較的類似している.
クローズド・パッケージ式の構文は、次の一般的な形式です.
 { (parameters) -> returnType in
     statements
}

例を挙げます.
func backwards(s1: String, s2: String) -> Bool {
    return s1 > s2
}
var reversed = names.sort(backwards)

上記の例に対応する閉パッケージ式バージョンのコード:
 reversed = names.sort({ (s1: String, s2: String) -> Bool in
    return s1 > s2
})

1.閉包特性
1.1コンテキスト推定タイプ(Inferring Type From Context)
いずれの場合も、関数やメソッドにパラメータとしてインライン閉パケット式で構成された閉パケットが渡されると、閉パケットのパラメータと戻り値のタイプが推定されます.
 reversed = names.sort( { s1, s2 in return s1 > s2 } )


1.2単式閉包暗黙的復帰(Implicit Return From Single-Expression Clossures)
単行式閉パケットは、returnキーワードを省略することによって、単行式の結果を暗黙的に返すことができる.
reversed = names.sort( { s1, s2 in s1 > s2 } )

1.3パラメータ名の略語(Shorthand Argument Name)
Swiftは自動的にインライン閉パケットにパラメータ名の略語機能を提供し、閉パケットのパラメータを$0,$1,$2で直接呼び出すことができます.
reversed = names.sort( { $0 > $1 } )

P.S.さらに簡素化できます
演算子関数(Operator Functions):
SwiftのStringタイプは、1つの関数として2つのStringタイプのパラメータを受け入れ、Boolタイプの値を返す、番号(>)より大きい文字列実装を定義する.
reversed = names.sort(>)

2.フォロークローズ(Trailing Closures)
長い閉パケット式を最後のパラメータとして関数に渡す必要がある場合は、後続の閉パケットを使用して関数の可読性を向上させることができます.
//    
func someFunctionThatTakesAClosure(closure: () -> Void) { 
    //      
}

//                 
someFunctionThatTakesAClosure({
    //        
})

//                 
someFunctionThatTakesAClosure() {
    //        
}
//         :
reversed = names.sort() { $0 > $1 }

//                ,        ,      ()    :
reversed = names.sort { $0 > $1 }

3.キャプチャ(capturing)
閉パッケージは、そのコンテキスト内の任意の定数および変数の参照をキャプチャおよび格納します.Swiftは、取得中に関連するすべてのメモリ操作を管理します.これには、不要な変数を解放することも含まれます.
これらの定数と変数を定義する元の役割ドメインが存在しなくても、閉パケットは閉パケット関数内でこれらの値を参照および変更できます.
Swiftでは,値をキャプチャできる閉パケットの最も単純な形式はネスト関数,すなわち他の関数の関数体内に定義された関数である.ネストされた関数は、外部関数のすべてのパラメータと定義された定数と変数をキャプチャします.
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
     var runningTotal = 0
     //      
     func incrementor() -> Int { 
         runningTotal += amount //            amount runningTotal  
         return runningTotal
     }
     return incrementor
 }

let incrementByTen = makeIncrementor(forIncrement: 10)

incrementByTen() //      10
incrementByTen() //      20
incrementByTen() //      30


P.S上のメソッド呼び出しは、makeIncrementor関数体がclassであり、incrementorがclassの関数であり、incrementor関数がclassの属性amountとrunningTotalを参照していると解釈できる.
別のincrementorが作成されている場合は、独自の新しい独立したrunningTotal変数への参照があります.
let incrementBySeven = makeIncrementor(forIncrement: 7)
incrementBySeven() //      7

4.クローズは参照タイプ(Closures Are Reference Type)
上記の例では、incrementBySevenおよびincrementByTenは定数ですが、これらの定数が指す閉パケットは、取得した変数の値を増加させることができます.これは、関数と閉パッケージが参照タイプであるためです.関数または閉パケットを定数または変数に割り当てても、実際には定数または変数の値を対応する関数または閉パケットの参照に設定します.上記の例では、閉パケットへの参照incrementByTenは、閉パケットコンテンツ自体ではなく定数である.
これは、2つの異なる定数または変数に閉パケットを割り当てると、2つの値が同じ閉パケットを指すことを意味します.
5.非脱出閉鎖(Nonescaping Closures)
閉包がパラメータとして関数に伝達されるが,この閉包は関数が戻った後に実行され,この閉包は関数から逃れると呼ぶ.パラメータ名の前に@noescapeとマークして、この閉パケットがこの関数から「脱出」できないことを示すことができます.閉パッケージタグ@noescapeを使用すると、コンパイラにこの閉パッケージのライフサイクル(閉パッケージは関数体でのみ実行され、関数体から離れて実行できないため、コンパイラは実行時のコンテキストを明確に知っている)を知ることができ、比較的急進的な最適化を行うことができます.
func someFunctionWithNoescapeClosure(@noescape closure: () -> Void) {
    closure()
}

//                      
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: () -> Void) {
    completionHandlers.append(completionHandler)
}

class SomeClass {
    var x = 10
    func doSomething() {
        //       @noescape             self
        someFunctionWithEscapingClosure { self.x = 100 } 
        someFunctionWithNoescapeClosure { x = 200 }
    }
}

let instance = SomeClass()

instance.doSomething()
print(instance.x) // prints "200"

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

6.自動クローズ(Autoclosures)
この閉パケットはパラメータを受け入れず、呼び出されるとパッケージされた式の値が返されます.
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

// customerProvider       String ,   () -> String 
let customerProvider = { customersInLine.removeAtIndex(0) }

print("Now serving \(customerProvider())!") // prints "Now serving Chris!"
print(customersInLine.count)   // prints "4"
// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serveCustomer(customerProvider: () -> String) {
    print("Now serving \(customerProvider())!")
}
serveCustomer( { customersInLine.removeAtIndex(0) } )
// prints "Now serving Alex!"


  @autoclosure
func serveCustomer(@autoclosure customerProvider: () -> String) {
     print("Now serving \(customerProvider())!")
}

serveCustomer(customersInLine.removeAtIndex(0))
 // prints "Now serving Ewa!"

@autoclosureプロパティには@noescapeプロパティが暗黙的に含まれています.この閉パッケージを「逃走」させたい場合は、@autoclosureプロパティを使用する必要があります.
参考資料:The Swift Programming Language