63
48

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Swift(一人)Advent Calendar 2015

Day 2

autoclosureというマニアックで少し面白い機能

Last updated at Posted at 2015-12-01

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
}

参考URL

Closures

63
48
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
63
48

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?