自動クロージャは、引数として関数に渡される式をラップするために、自動的に生成されるクロージャです。引数を受け取らずに呼び出されたとき、内側にラップされた式の値を返します。このシンタックスの利便性によって、明示的なクロージャの代わりに通常の式を記述して、関数のパラメータを囲む波括弧を省略することができます。
自動クロージャを受け取る関数を呼び出すのは一般的ですが、その種の関数を実装することは一般的ではありません。例えば、関数 assert(condition:message:file:line:)
は condition
と message
パラメータに自動クロージャを受け取ります。condition
パラメータはデバッグ用のビルドでのみ評価され、message
プロパティは condition
が false
の場合にのみ評価されます。
クロージャを呼び出すまで内側のコードは実行されないため、自動クロージャは評価を遅らせます。評価を遅らせることは、コードが実行されるタイミングをコントロールできるようになるため、副作用があるコードや、計算コストが高いコードにとって有益です。次のコードは、クロージャが評価を遅延させる方法を示しています。
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// "5" と出力
let customerProvider = { customersInLine.removeAtIndex(0) }
print(customersInLine.count)
// "5" と出力
print("Now serving \(customerProvider())!")
// "Now serving Chris!" と出力
print(customersInLine.count)
// "4" と出力
配列 customersInLine
の最初の要素はクロージャ内のコードによって削除されていますが、クロージャが実際に呼び出されるまでは、配列の要素は削除されません。クロージャが呼び出されない場合には、クロージャ内の式が評価されることはなく、配列の要素が削除されることはありません。customerProvider
の型は String
ではなく、パラメータが無く文字列を返す関数 () -> String
であることに注意してください。
引数として関数にクロージャを渡すときも、同じように遅延評価となります。
// customersInLine は ["Alex", "Ewa", "Barry", "Daniella"]
func serveCustomer(customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serveCustomer( { customersInLine.removeAtIndex(0) } )
// "Now serving Alex!" と出力
このコード例の関数 serveCustomer(_:)
は、顧客の名前を返すクロージャを受け取っています。serveCustomer(_:)
の次のバージョンは同じ処理を実行しますが、クロージャを受け取る代わりに、パラメータに @autoclosure
属性を付けて自動クロージャを受け取ります。クロージャの代わりに String
引数を受け取るように関数を呼び出すことができます。customerProvider
パラメータに @autoclosure
属性が付けられているため、引数は自動的にクロージャに変換されます。
// customersInLine は ["Ewa", "Barry", "Daniella"]
func serveCustomer(@autoclosure customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serveCustomer(customersInLine.removeAtIndex(0))
// "Now serving Ewa!" と出力
@autoclosure
属性は、Nonescaping Closures で説明されている @noescape
属性を伴います。自動クロージャをエスケープさせたい場合、属性の @autoclosure(escaping)
形式を使用します。
// customersInLine は ["Barry", "Daniella"]
var customerProviders: [() -> String] = []
func collectCustomerProviders(@autoclosure(escaping) customerProvider: () -> String) {
customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.removeAtIndex(0))
collectCustomerProviders(customersInLine.removeAtIndex(0))
print("Collected \(customerProviders.count) closures.")
// prints "Collected 2 closures."
for customerProvider in customerProviders {
print("Now serving \(customerProvider())!")
}
// prints "Now serving Barry!"
// prints "Now serving Daniella!"
このコードでは、引数 customer
に渡されたクロージャを呼び出す代わりに、関数 collectCustomerProviders(_:)
は配列 customerProviders
にクロージャを追加します。配列は関数のスコープの外側で宣言されていて、関数のリターン後に配列内のクロージャを実行することができます。結果として、引数 customer
の値は関数のスコープをエスケープすることができています。
Portions of this page are translations based on work created and shared by Apple and used according to terms described in the Creative Commons Attribution 4.0 International License.