Swiftの @noescape をもっと使おう

  • 160
    いいね
  • 2
    コメント


(追記)
Swift 3 では @noescape の挙動がデフォルトになったため記述する必要がなくなりました。


よく知らなかったのですが、調べてみたら積極的に使っていこうという気になりました。

使用例

関数の引数で渡すクロージャにattributeとして付けます。

func action(@noescape closure: () -> ()) {
    closure()
}

(Appleのドキュメント)

@noescape を付けたクロージャに保証されること

  • クロージャはどこへも保持されない
  • クロージャは非同期的に後から実行されることはない

つまり、
クロージャの生存期間が関数よりも短い
ことを保証することになります。

よって、クロージャを渡した先で

  • クロージャをインスタンス変数などとして保持
  • クロージャを非同期的に後で実行

などする場合にはクロージャの生存期間が関数よりも延びてしまうので @noescape を付けることができません

(StackOverflowの回答)

いいこと

クロージャ内で self. を書かなくてよくなります。

more importantly disables the “self.” requirement in closure arguments.

(リリースノートより)

以下のような理由からではないかと思われます。

  • self. の記述はクロージャの生存が終わるまではselfが開放されないことを明示するためのもの(たぶん)
  • @noescape が付いたクロージャは生存がすぐに終わる
  • クロージャの生存が終わってもselfは必ず存在する
  • つまり、 self. を書いてselfが開放されないことを明示する必要がない

self. を書く必要がないということは [weak self][unowned self] について考える必要がない、つまり selfについての循環参照を考える必要がなくなる ということです!

@noescape なしの例

func action(closure: () -> ()) {
    closure()
}

class A {
    var count = 0
    func countAfterAction() {
        action {
            self.count++ // ここ
        }
    }
}

@noescape ありの例

func action(@noescape closure: () -> ()) {
    closure()
}

class A {
    var count = 0
    func countAfterAction() {
        action {
            count++ // ここ
        }
    }
}

またパフォーマンスが良くなる効果もあるようです。

This enables some minor performance optimizations

(リリースノートより)

結論

@noescape を付けられる場合は @noescape を付けていきたい。

  • クロージャ内で self. を書く必要がなくなる
  • つまり、selfの循環参照の問題を気にしなくてよくなる
  • パフォーマンスが良くなる

(おまけ) noescapeの意味

  • 生き延びない (escapeしない) という意味みたいです。

This indicates that the parameter is only ever called (or passed as an @noescape parameter in a call), which means that it cannot outlive the lifetime of the call.

(リリースノートより)

環境

Apple Swift version 2.1 (swiftlang-700.1.101.6 clang-700.1.76)
Target: x86_64-apple-darwin14.5.0
この投稿は Swift その3 Advent Calendar 20154日目の記事です。