autoclosureとは
Swiftにはautoclosureという機能があります。
どういう機能かと言うと、下のように呼び出し側で渡した値をメソッド内ではクロージャーとして使えるものです。
method(1)
func method(@autoclosure closure: () -> Int) {
closure() // → 1
}
@autoclosureを使わない場合は下のように書く必要があります。
method({ return 1 })
func method(closure: () -> Int) {
closure() // → 1
}
autoclosureが使われている所
上の説明だけだと「これは何が嬉しいの??」という感じなので実際に使われているところを紹介します。
使われているところはSwiftの標準メソッドです。
具体例としては&&演算子があります。
&&演算子とautoclosure
Swiftでは&&演算子はグローバルな関数です。
public func &&<T : BooleanType, U : BooleanType>(lhs: T, @autoclosure rhs: () throws -> U) rethrows -> Bool
+-*/ など全ての演算子も同様です。
func +(lhs: Int, rhs: Int) -> Int
func -(lhs: Int, rhs: Int) -> Int
func *(lhs: Int, rhs: Int) -> Int
func /(lhs: Int, rhs: Int) -> Int
&&の両辺は引数なので、&&を普通に実装すると常にアプリがクラッシュします。
// autoclosureを使わない場合の&&の実装内容(予想)
func &&(left: Bool, right: Bool) -> Bool {
if !left { return false }
return right
}
let value: Int? = nil
// これは&&の1つ目の条件がtrue/falseに関わらず2つ目の条件は実行されるのでクラッシュする
let result = (value != nil && value! == 1)
これを回避する為には、&&の2番目の引数をクロージャーにして遅延実行する必要があります。
func &&(left: Bool, right: () -> Bool) -> Bool {
if !left { return false }
return right()
}
let value: Int? = nil
let result = (value != nil && { value! == 1 })
これで落ちなくはなりましたが、&&の右側に常に{}を付けるのはめんどくさいです。
しかし@autoclosureを使えば引数を自動でクロージャーに変換してくれるので{}を省略できてすっきりできます。
func &&(left: Bool, @autoclosure right: () -> Bool) -> Bool {
if !left { return false }
return right()
}
let value: Int? = nil
let result = (value != nil && value! == 1)
autoclosureの細かい仕様
autoclosureを使ったメソッドの引数の型はクロージャーの戻り値の型と等しくなります。
method(1) // → closureの戻り値がIntなので引数の型はInt型
func method(@autoclosure closure: () -> Int) {
print(closure()) // 1
}
クロージャーの引数はVoidにする必要があります。
// closureの引数を指定するとコンパイルエラー
func method(@autoclosure closure: (Int) -> Int) {
print(closure()) // 1
}